/*
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

/** \file
 * \ingroup RNA
 */

#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <ctype.h>

#include "MEM_guardedalloc.h"

#include "DNA_ID.h"
#include "DNA_scene_types.h"
#include "DNA_constraint_types.h"
#include "DNA_modifier_types.h"
#include "DNA_windowmanager_types.h"

#include "BLI_blenlib.h"
#include "BLI_utildefines.h"
#include "BLI_dynstr.h"
#include "BLI_ghash.h"
#include "BLI_math.h"

#include "BLF_api.h"
#include "BLT_translation.h"

#include "BKE_animsys.h"
#include "BKE_collection.h"
#include "BKE_context.h"
#include "BKE_idcode.h"
#include "BKE_idprop.h"
#include "BKE_fcurve.h"
#include "BKE_main.h"
#include "BKE_report.h"
#include "BKE_node.h"

#include "DEG_depsgraph.h"

#include "RNA_access.h"
#include "RNA_define.h"
#include "RNA_enum_types.h"

#include "WM_api.h"
#include "WM_message.h"

/* flush updates */
#include "DNA_object_types.h"
#include "WM_types.h"

#include "rna_internal.h"
#include "rna_access_internal.h"

const PointerRNA PointerRNA_NULL = {NULL};

/* Init/Exit */

void RNA_init(void)
{
  StructRNA *srna;
  PropertyRNA *prop;

  BLENDER_RNA.structs_map = BLI_ghash_str_new_ex(__func__, 2048);
  BLENDER_RNA.structs_len = 0;

  for (srna = BLENDER_RNA.structs.first; srna; srna = srna->cont.next) {
    if (!srna->cont.prophash) {
      srna->cont.prophash = BLI_ghash_str_new("RNA_init gh");

      for (prop = srna->cont.properties.first; prop; prop = prop->next) {
        if (!(prop->flag_internal & PROP_INTERN_BUILTIN)) {
          BLI_ghash_insert(srna->cont.prophash, (void *)prop->identifier, prop);
        }
      }
    }
    BLI_assert(srna->flag & STRUCT_PUBLIC_NAMESPACE);
    BLI_ghash_insert(BLENDER_RNA.structs_map, (void *)srna->identifier, srna);
    BLENDER_RNA.structs_len += 1;
  }
}

void RNA_exit(void)
{
  StructRNA *srna;

  RNA_property_update_cache_free();

  for (srna = BLENDER_RNA.structs.first; srna; srna = srna->cont.next) {
    if (srna->cont.prophash) {
      BLI_ghash_free(srna->cont.prophash, NULL, NULL);
      srna->cont.prophash = NULL;
    }
  }

  RNA_free(&BLENDER_RNA);
}

/* Pointer */

void RNA_main_pointer_create(struct Main *main, PointerRNA *r_ptr)
{
  r_ptr->owner_id = NULL;
  r_ptr->type = &RNA_BlendData;
  r_ptr->data = main;
}

void RNA_id_pointer_create(ID *id, PointerRNA *r_ptr)
{
  StructRNA *type, *idtype = NULL;

  if (id) {
    PointerRNA tmp = {NULL};
    tmp.data = id;
    idtype = rna_ID_refine(&tmp);

    while (idtype->refine) {
      type = idtype->refine(&tmp);

      if (type == idtype) {
        break;
      }
      else {
        idtype = type;
      }
    }
  }

  r_ptr->owner_id = id;
  r_ptr->type = idtype;
  r_ptr->data = id;
}

void RNA_pointer_create(ID *id, StructRNA *type, void *data, PointerRNA *r_ptr)
{
#if 0 /* UNUSED */
  StructRNA *idtype = NULL;

  if (id) {
    PointerRNA tmp = {0};
    tmp.data = id;
    idtype = rna_ID_refine(&tmp);
  }
#endif

  r_ptr->owner_id = id;
  r_ptr->type = type;
  r_ptr->data = data;

  if (data) {
    while (r_ptr->type && r_ptr->type->refine) {
      StructRNA *rtype = r_ptr->type->refine(r_ptr);

      if (rtype == r_ptr->type) {
        break;
      }
      else {
        r_ptr->type = rtype;
      }
    }
  }
}

bool RNA_pointer_is_null(const PointerRNA *ptr)
{
  return !((ptr->data != NULL) && (ptr->owner_id != NULL) && (ptr->type != NULL));
}

static void rna_pointer_inherit_id(StructRNA *type, PointerRNA *parent, PointerRNA *ptr)
{
  if (type && type->flag & STRUCT_ID) {
    ptr->owner_id = ptr->data;
  }
  else {
    ptr->owner_id = parent->owner_id;
  }
}

void RNA_blender_rna_pointer_create(PointerRNA *r_ptr)
{
  r_ptr->owner_id = NULL;
  r_ptr->type = &RNA_BlenderRNA;
  r_ptr->data = &BLENDER_RNA;
}

PointerRNA rna_pointer_inherit_refine(PointerRNA *ptr, StructRNA *type, void *data)
{
  if (data) {
    PointerRNA result;
    result.data = data;
    result.type = type;
    rna_pointer_inherit_id(type, ptr, &result);

    while (result.type->refine) {
      type = result.type->refine(&result);

      if (type == result.type) {
        break;
      }
      else {
        result.type = type;
      }
    }
    return result;
  }
  else {
    return PointerRNA_NULL;
  }
}

void RNA_pointer_recast(PointerRNA *ptr, PointerRNA *r_ptr)
{
#if 0 /* works but this case if covered by more general code below. */
  if (RNA_struct_is_ID(ptr->type)) {
    /* simple case */
    RNA_id_pointer_create(ptr->owner_id, r_ptr);
  }
  else
#endif
  {
    StructRNA *base;
    PointerRNA t_ptr;
    *r_ptr = *ptr; /* initialize as the same in case cant recast */

    for (base = ptr->type->base; base; base = base->base) {
      t_ptr = rna_pointer_inherit_refine(ptr, base, ptr->data);
      if (t_ptr.type && t_ptr.type != ptr->type) {
        *r_ptr = t_ptr;
      }
    }
  }
}

/* ID Properties */

void rna_idproperty_touch(IDProperty *idprop)
{
  /* so the property is seen as 'set' by rna */
  idprop->flag &= ~IDP_FLAG_GHOST;
}

static IDProperty *rna_idproperty_ui_container(PropertyRNA *prop)
{
  IDProperty *idprop;

  for (idprop = ((IDProperty *)prop)->prev; idprop; idprop = idprop->prev) {
    if (STREQ(RNA_IDP_UI, idprop->name)) {
      break;
    }
  }

  if (idprop == NULL) {
    for (idprop = ((IDProperty *)prop)->next; idprop; idprop = idprop->next) {
      if (STREQ(RNA_IDP_UI, idprop->name)) {
        break;
      }
    }
  }

  return idprop;
}

/* return a UI local ID prop definition for this prop */
static IDProperty *rna_idproperty_ui(PropertyRNA *prop)
{
  IDProperty *idprop = rna_idproperty_ui_container(prop);

  if (idprop) {
    return IDP_GetPropertyTypeFromGroup(idprop, ((IDProperty *)prop)->name, IDP_GROUP);
  }

  return NULL;
}

/* return or create a UI local ID prop definition for this prop */
static IDProperty *rna_idproperty_ui_ensure(PointerRNA *ptr, PropertyRNA *prop, bool create)
{
  IDProperty *idprop = rna_idproperty_ui_container(prop);
  IDPropertyTemplate dummy = {0};

  if (idprop == NULL && create) {
    IDProperty *props = RNA_struct_idprops(ptr, false);

    /* Sanity check: props is the actual container of this property. */
    if (props != NULL && BLI_findindex(&props->data.group, prop) >= 0) {
      idprop = IDP_New(IDP_GROUP, &dummy, RNA_IDP_UI);

      if (!IDP_AddToGroup(props, idprop)) {
        IDP_FreePropertyContent(idprop);
        return NULL;
      }
    }
  }

  if (idprop) {
    const char *name = ((IDProperty *)prop)->name;
    IDProperty *rv = IDP_GetPropertyTypeFromGroup(idprop, name, IDP_GROUP);

    if (rv == NULL && create) {
      rv = IDP_New(IDP_GROUP, &dummy, name);

      if (!IDP_AddToGroup(idprop, rv)) {
        IDP_FreePropertyContent(rv);
        return NULL;
      }
    }

    return rv;
  }

  return NULL;
}

static bool rna_idproperty_ui_set_default(PointerRNA *ptr,
                                          PropertyRNA *prop,
                                          const char type,
                                          IDPropertyTemplate *value)
{
  BLI_assert(ELEM(type, IDP_INT, IDP_DOUBLE));

  if (prop->magic == RNA_MAGIC) {
    return false;
  }

  /* attempt to get the local ID values */
  IDProperty *idp_ui = rna_idproperty_ui_ensure(ptr, prop, value != NULL);

  if (idp_ui == NULL) {
    return (value == NULL);
  }

  IDProperty *item = IDP_GetPropertyTypeFromGroup(idp_ui, "default", type);

  if (value == NULL) {
    if (item != NULL) {
      IDP_RemoveFromGroup(idp_ui, item);
    }
  }
  else {
    if (item != NULL) {
      switch (type) {
        case IDP_INT:
          IDP_Int(item) = value->i;
          break;
        case IDP_DOUBLE:
          IDP_Double(item) = value->d;
          break;
        default:
          BLI_assert(false);
          return false;
      }
    }
    else {
      item = IDP_New(type, value, "default");

      if (!IDP_AddToGroup(idp_ui, item)) {
        IDP_FreePropertyContent(item);
        return false;
      }
    }
  }

  return true;
}

IDProperty *RNA_struct_idprops(PointerRNA *ptr, bool create)
{
  StructRNA *type = ptr->type;

  if (type && type->idproperties) {
    return type->idproperties(ptr, create);
  }

  return NULL;
}

bool RNA_struct_idprops_check(StructRNA *srna)
{
  return (srna && srna->idproperties);
}

IDProperty *rna_idproperty_find(PointerRNA *ptr, const char *name)
{
  IDProperty *group = RNA_struct_idprops(ptr, 0);

  if (group) {
    if (group->type == IDP_GROUP) {
      return IDP_GetPropertyFromGroup(group, name);
    }
    else {
      /* Not sure why that happens sometimes, with nested properties... */
      /* Seems to be actually array prop, name is usually "0"... To be sorted out later. */
#if 0
      printf(
          "Got unexpected IDProp container when trying to retrieve %s: %d\n", name, group->type);
#endif
    }
  }

  return NULL;
}

static void rna_idproperty_free(PointerRNA *ptr, const char *name)
{
  IDProperty *group = RNA_struct_idprops(ptr, 0);

  if (group) {
    IDProperty *idprop = IDP_GetPropertyFromGroup(group, name);
    if (idprop) {
      IDP_FreeFromGroup(group, idprop);
    }
  }
}

static int rna_ensure_property_array_length(PointerRNA *ptr, PropertyRNA *prop)
{
  if (prop->magic == RNA_MAGIC) {
    int arraylen[RNA_MAX_ARRAY_DIMENSION];
    return (prop->getlength && ptr->data) ? prop->getlength(ptr, arraylen) : prop->totarraylength;
  }
  else {
    IDProperty *idprop = (IDProperty *)prop;

    if (idprop->type == IDP_ARRAY) {
      return idprop->len;
    }
    else {
      return 0;
    }
  }
}

static bool rna_ensure_property_array_check(PropertyRNA *prop)
{
  if (prop->magic == RNA_MAGIC) {
    return (prop->getlength || prop->totarraylength);
  }
  else {
    IDProperty *idprop = (IDProperty *)prop;

    return (idprop->type == IDP_ARRAY);
  }
}

static void rna_ensure_property_multi_array_length(PointerRNA *ptr,
                                                   PropertyRNA *prop,
                                                   int length[])
{
  if (prop->magic == RNA_MAGIC) {
    if (prop->getlength) {
      prop->getlength(ptr, length);
    }
    else {
      memcpy(length, prop->arraylength, prop->arraydimension * sizeof(int));
    }
  }
  else {
    IDProperty *idprop = (IDProperty *)prop;

    if (idprop->type == IDP_ARRAY) {
      length[0] = idprop->len;
    }
    else {
      length[0] = 0;
    }
  }
}

static bool rna_idproperty_verify_valid(PointerRNA *ptr, PropertyRNA *prop, IDProperty *idprop)
{
  /* this verifies if the idproperty actually matches the property
   * description and otherwise removes it. this is to ensure that
   * rna property access is type safe, e.g. if you defined the rna
   * to have a certain array length you can count on that staying so */

  switch (idprop->type) {
    case IDP_IDPARRAY:
      if (prop->type != PROP_COLLECTION) {
        return false;
      }
      break;
    case IDP_ARRAY:
      if (rna_ensure_property_array_length(ptr, prop) != idprop->len) {
        return false;
      }

      if (idprop->subtype == IDP_FLOAT && prop->type != PROP_FLOAT) {
        return false;
      }
      if (idprop->subtype == IDP_INT && !ELEM(prop->type, PROP_BOOLEAN, PROP_INT, PROP_ENUM)) {
        return false;
      }

      break;
    case IDP_INT:
      if (!ELEM(prop->type, PROP_BOOLEAN, PROP_INT, PROP_ENUM)) {
        return false;
      }
      break;
    case IDP_FLOAT:
    case IDP_DOUBLE:
      if (prop->type != PROP_FLOAT) {
        return false;
      }
      break;
    case IDP_STRING:
      if (prop->type != PROP_STRING) {
        return false;
      }
      break;
    case IDP_GROUP:
    case IDP_ID:
      if (prop->type != PROP_POINTER) {
        return false;
      }
      break;
    default:
      return false;
  }

  return true;
}

static PropertyRNA *typemap[IDP_NUMTYPES] = {
    (PropertyRNA *)&rna_PropertyGroupItem_string,
    (PropertyRNA *)&rna_PropertyGroupItem_int,
    (PropertyRNA *)&rna_PropertyGroupItem_float,
    NULL,
    NULL,
    NULL,
    (PropertyRNA *)&rna_PropertyGroupItem_group,
    (PropertyRNA *)&rna_PropertyGroupItem_id,
    (PropertyRNA *)&rna_PropertyGroupItem_double,
    (PropertyRNA *)&rna_PropertyGroupItem_idp_array,
};

static PropertyRNA *arraytypemap[IDP_NUMTYPES] = {
    NULL,
    (PropertyRNA *)&rna_PropertyGroupItem_int_array,
    (PropertyRNA *)&rna_PropertyGroupItem_float_array,
    NULL,
    NULL,
    NULL,
    (PropertyRNA *)&rna_PropertyGroupItem_collection,
    NULL,
    (PropertyRNA *)&rna_PropertyGroupItem_double_array,
};

static void *rna_idproperty_check_ex(PropertyRNA **prop,
                                     PointerRNA *ptr,
                                     const bool return_rnaprop)
{
  /* This is quite a hack, but avoids some complexity in the API. we
   * pass IDProperty structs as PropertyRNA pointers to the outside.
   * We store some bytes in PropertyRNA structs that allows us to
   * distinguish it from IDProperty structs. If it is an ID property,
   * we look up an IDP PropertyRNA based on the type, and set the data
   * pointer to the IDProperty. */

  if ((*prop)->magic == RNA_MAGIC) {
    if ((*prop)->flag & PROP_IDPROPERTY) {
      IDProperty *idprop = rna_idproperty_find(ptr, (*prop)->identifier);

      if (idprop && !rna_idproperty_verify_valid(ptr, *prop, idprop)) {
        IDProperty *group = RNA_struct_idprops(ptr, 0);

        IDP_FreeFromGroup(group, idprop);
        return NULL;
      }

      return idprop;
    }
    else {
      return return_rnaprop ? *prop : NULL;
    }
  }

  {
    IDProperty *idprop = (IDProperty *)(*prop);

    if (idprop->type == IDP_ARRAY) {
      *prop = arraytypemap[(int)(idprop->subtype)];
    }
    else {
      *prop = typemap[(int)(idprop->type)];
    }

    return idprop;
  }
}

/* This function only returns an IDProperty,
 * or NULL (in case IDProp could not be found, or prop is a real RNA property). */
IDProperty *rna_idproperty_check(PropertyRNA **prop, PointerRNA *ptr)
{
  return rna_idproperty_check_ex(prop, ptr, false);
}

/* This function always return the valid, real data pointer, be it a regular RNA property one,
 * or an IDProperty one. */
PropertyRNA *rna_ensure_property_realdata(PropertyRNA **prop, PointerRNA *ptr)
{
  return rna_idproperty_check_ex(prop, ptr, true);
}

PropertyRNA *rna_ensure_property(PropertyRNA *prop)
{
  /* the quick version if we don't need the idproperty */

  if (prop->magic == RNA_MAGIC) {
    return prop;
  }

  {
    IDProperty *idprop = (IDProperty *)prop;

    if (idprop->type == IDP_ARRAY) {
      return arraytypemap[(int)(idprop->subtype)];
    }
    else {
      return typemap[(int)(idprop->type)];
    }
  }
}

static const char *rna_ensure_property_identifier(const PropertyRNA *prop)
{
  if (prop->magic == RNA_MAGIC) {
    return prop->identifier;
  }
  else {
    return ((const IDProperty *)prop)->name;
  }
}

static const char *rna_ensure_property_description(PropertyRNA *prop)
{
  const char *description = NULL;

  if (prop->magic == RNA_MAGIC) {
    description = prop->description;
  }
  else {
    /* attempt to get the local ID values */
    IDProperty *idp_ui = rna_idproperty_ui(prop);

    if (idp_ui) {
      IDProperty *item = IDP_GetPropertyTypeFromGroup(idp_ui, "description", IDP_STRING);
      if (item) {
        description = IDP_String(item);
      }
    }

    if (description == NULL) {
      description = ((IDProperty *)prop)->name; /* XXX - not correct */
    }
  }

  return description;
}

static const char *rna_ensure_property_name(const PropertyRNA *prop)
{
  const char *name;

  if (prop->magic == RNA_MAGIC) {
    name = prop->name;
  }
  else {
    name = ((const IDProperty *)prop)->name;
  }

  return name;
}

/* Structs */

StructRNA *RNA_struct_find(const char *identifier)
{
  return BLI_ghash_lookup(BLENDER_RNA.structs_map, identifier);
}

const char *RNA_struct_identifier(const StructRNA *type)
{
  return type->identifier;
}

const char *RNA_struct_ui_name(const StructRNA *type)
{
  return CTX_IFACE_(type->translation_context, type->name);
}

const char *RNA_struct_ui_name_raw(const StructRNA *type)
{
  return type->name;
}

int RNA_struct_ui_icon(const StructRNA *type)
{
  if (type) {
    return type->icon;
  }
  else {
    return ICON_DOT;
  }
}

const char *RNA_struct_ui_description(const StructRNA *type)
{
  return TIP_(type->description);
}

const char *RNA_struct_ui_description_raw(const StructRNA *type)
{
  return type->description;
}

const char *RNA_struct_translation_context(const StructRNA *type)
{
  return type->translation_context;
}

PropertyRNA *RNA_struct_name_property(const StructRNA *type)
{
  return type->nameproperty;
}

const EnumPropertyItem *RNA_struct_property_tag_defines(const StructRNA *type)
{
  return type->prop_tag_defines;
}

PropertyRNA *RNA_struct_iterator_property(StructRNA *type)
{
  return type->iteratorproperty;
}

StructRNA *RNA_struct_base(StructRNA *type)
{
  return type->base;
}

/**
 * Use to find the subtype directly below a base-type.
 *
 * So if type were `RNA_SpotLIght`, `RNA_struct_base_of(type, &RNA_ID)` would return `&RNA_Light`.
 */
const StructRNA *RNA_struct_base_child_of(const StructRNA *type, const StructRNA *parent_type)
{
  while (type) {
    if (type->base == parent_type) {
      return type;
    }
    type = type->base;
  }
  return NULL;
}

bool RNA_struct_is_ID(const StructRNA *type)
{
  return (type->flag & STRUCT_ID) != 0;
}

bool RNA_struct_undo_check(const StructRNA *type)
{
  return (type->flag & STRUCT_UNDO) != 0;
}

bool RNA_struct_idprops_register_check(const StructRNA *type)
{
  return (type->flag & STRUCT_NO_IDPROPERTIES) == 0;
}

bool RNA_struct_idprops_datablock_allowed(const StructRNA *type)
{
  return (type->flag & (STRUCT_NO_DATABLOCK_IDPROPERTIES | STRUCT_NO_IDPROPERTIES)) == 0;
}

/**
 * Whether given type implies datablock usage by IDProperties.
 * This is used to prevent classes allowed to have IDProperties,
 * but not datablock ones, to indirectly use some
 * (e.g. by assigning an IDP_GROUP containing some IDP_ID pointers...).
 */
bool RNA_struct_idprops_contains_datablock(const StructRNA *type)
{
  return (type->flag & (STRUCT_CONTAINS_DATABLOCK_IDPROPERTIES | STRUCT_ID)) != 0;
}

/* remove an id-property */
bool RNA_struct_idprops_unset(PointerRNA *ptr, const char *identifier)
{
  IDProperty *group = RNA_struct_idprops(ptr, 0);

  if (group) {
    IDProperty *idp = IDP_GetPropertyFromGroup(group, identifier);
    if (idp) {
      IDP_FreeFromGroup(group, idp);

      return true;
    }
  }
  return false;
}

bool RNA_struct_is_a(const StructRNA *type, const StructRNA *srna)
{
  const StructRNA *base;

  if (srna == &RNA_AnyType) {
    return true;
  }

  if (!type) {
    return false;
  }

  /* ptr->type is always maximally refined */
  for (base = type; base; base = base->base) {
    if (base == srna) {
      return true;
    }
  }

  return false;
}

PropertyRNA *RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
{
  if (identifier[0] == '[' && identifier[1] == '"') { /* "  (dummy comment to avoid confusing some
                                                       * function lists in text editors) */
    /* id prop lookup, not so common */
    PropertyRNA *r_prop = NULL;
    PointerRNA r_ptr; /* only support single level props */
    if (RNA_path_resolve_property(ptr, identifier, &r_ptr, &r_prop) && (r_ptr.type == ptr->type) &&
        (r_ptr.data == ptr->data)) {
      return r_prop;
    }
  }
  else {
    /* most common case */
    PropertyRNA *iterprop = RNA_struct_iterator_property(ptr->type);
    PointerRNA propptr;

    if (RNA_property_collection_lookup_string(ptr, iterprop, identifier, &propptr)) {
      return propptr.data;
    }
  }

  return NULL;
}

/* Find the property which uses the given nested struct */
static PropertyRNA *RNA_struct_find_nested(PointerRNA *ptr, StructRNA *srna)
{
  PropertyRNA *prop = NULL;

  RNA_STRUCT_BEGIN (ptr, iprop) {
    /* This assumes that there can only be one user of this nested struct */
    if (RNA_property_pointer_type(ptr, iprop) == srna) {
      prop = iprop;
      break;
    }
  }
  RNA_PROP_END;

  return prop;
}

bool RNA_struct_contains_property(PointerRNA *ptr, PropertyRNA *prop_test)
{
  /* note, prop_test could be freed memory, only use for comparison */

  /* validate the RNA is ok */
  PropertyRNA *iterprop;
  bool found = false;

  iterprop = RNA_struct_iterator_property(ptr->type);

  RNA_PROP_BEGIN (ptr, itemptr, iterprop) {
    /* PropertyRNA *prop = itemptr.data; */
    if (prop_test == (PropertyRNA *)itemptr.data) {
      found = true;
      break;
    }
  }
  RNA_PROP_END;

  return found;
}

unsigned int RNA_struct_count_properties(StructRNA *srna)
{
  PointerRNA struct_ptr;
  unsigned int counter = 0;

  RNA_pointer_create(NULL, srna, NULL, &struct_ptr);

  RNA_STRUCT_BEGIN (&struct_ptr, prop) {
    counter++;
    UNUSED_VARS(prop);
  }
  RNA_STRUCT_END;

  return counter;
}

/* Low level direct access to type->properties,
 * note this ignores parent classes so should be used with care. */
const struct ListBase *RNA_struct_type_properties(StructRNA *srna)
{
  return &srna->cont.properties;
}

PropertyRNA *RNA_struct_type_find_property(StructRNA *srna, const char *identifier)
{
  return BLI_findstring_ptr(&srna->cont.properties, identifier, offsetof(PropertyRNA, identifier));
}

FunctionRNA *RNA_struct_find_function(StructRNA *srna, const char *identifier)
{
#if 1
  FunctionRNA *func;
  StructRNA *type;
  for (type = srna; type; type = type->base) {
    func = (FunctionRNA *)BLI_findstring_ptr(
        &type->functions, identifier, offsetof(FunctionRNA, identifier));
    if (func) {
      return func;
    }
  }
  return NULL;

  /* functional but slow */
#else
  PointerRNA tptr;
  PropertyRNA *iterprop;
  FunctionRNA *func;

  RNA_pointer_create(NULL, &RNA_Struct, srna, &tptr);
  iterprop = RNA_struct_find_property(&tptr, "functions");

  func = NULL;

  RNA_PROP_BEGIN (&tptr, funcptr, iterprop) {
    if (STREQ(identifier, RNA_function_identifier(funcptr.data))) {
      func = funcptr.data;
      break;
    }
  }
  RNA_PROP_END;

  return func;
#endif
}

const ListBase *RNA_struct_type_functions(StructRNA *srna)
{
  return &srna->functions;
}

StructRegisterFunc RNA_struct_register(StructRNA *type)
{
  return type->reg;
}

StructUnregisterFunc RNA_struct_unregister(StructRNA *type)
{
  do {
    if (type->unreg) {
      return type->unreg;
    }
  } while ((type = type->base));

  return NULL;
}

void **RNA_struct_instance(PointerRNA *ptr)
{
  StructRNA *type = ptr->type;

  do {
    if (type->instance) {
      return type->instance(ptr);
    }
  } while ((type = type->base));

  return NULL;
}

void *RNA_struct_py_type_get(StructRNA *srna)
{
  return srna->py_type;
}

void RNA_struct_py_type_set(StructRNA *srna, void *py_type)
{
  srna->py_type = py_type;
}

void *RNA_struct_blender_type_get(StructRNA *srna)
{
  return srna->blender_type;
}

void RNA_struct_blender_type_set(StructRNA *srna, void *blender_type)
{
  srna->blender_type = blender_type;
}

char *RNA_struct_name_get_alloc(PointerRNA *ptr, char *fixedbuf, int fixedlen, int *r_len)
{
  PropertyRNA *nameprop;

  if (ptr->data && (nameprop = RNA_struct_name_property(ptr->type))) {
    return RNA_property_string_get_alloc(ptr, nameprop, fixedbuf, fixedlen, r_len);
  }

  return NULL;
}

/**
 * Use when registering structs with the #STRUCT_PUBLIC_NAMESPACE flag.
 */
bool RNA_struct_available_or_report(ReportList *reports, const char *identifier)
{
  const StructRNA *srna_exists = RNA_struct_find(identifier);
  if (UNLIKELY(srna_exists != NULL)) {
    /* Use comprehensive string construction since this is such a rare occurrence
     * and information here may cut down time troubleshooting. */
    DynStr *dynstr = BLI_dynstr_new();
    BLI_dynstr_appendf(dynstr, "Type identifier '%s' is already in use: '", identifier);
    BLI_dynstr_append(dynstr, srna_exists->identifier);
    int i = 0;
    if (srna_exists->base) {
      for (const StructRNA *base = srna_exists->base; base; base = base->base) {
        BLI_dynstr_append(dynstr, "(");
        BLI_dynstr_append(dynstr, base->identifier);
        i += 1;
      }
      while (i--) {
        BLI_dynstr_append(dynstr, ")");
      }
    }
    BLI_dynstr_append(dynstr, "'.");
    char *result = BLI_dynstr_get_cstring(dynstr);
    BLI_dynstr_free(dynstr);
    BKE_report(reports, RPT_ERROR, result);
    MEM_freeN(result);
    return false;
  }
  else {
    return true;
  }
}

bool RNA_struct_bl_idname_ok_or_report(ReportList *reports,
                                       const char *identifier,
                                       const char *sep)
{
  const int len_sep = strlen(sep);
  const int len_id = strlen(identifier);
  const char *p = strstr(identifier, sep);
  /* TODO: make error, for now warning until add-ons update. */
#if 1
  const int report_level = RPT_WARNING;
  const bool failure = true;
#else
  const int report_level = RPT_ERROR;
  const bool failure = false;
#endif
  if (p == NULL || p == identifier || p + len_sep >= identifier + len_id) {
    BKE_reportf(
        reports, report_level, "'%s' doesn't contain '%s' with prefix & suffix", identifier, sep);
    return failure;
  }

  const char *c, *start, *end, *last;
  start = identifier;
  end = p;
  last = end - 1;
  for (c = start; c != end; c++) {
    if (((*c >= 'A' && *c <= 'Z') || ((c != start) && (*c >= '0' && *c <= '9')) ||
         ((c != start) && (c != last) && (*c == '_'))) == 0) {
      BKE_reportf(
          reports, report_level, "'%s' doesn't have upper case alpha-numeric prefix", identifier);
      return failure;
    }
  }

  start = p + len_sep;
  end = identifier + len_id;
  last = end - 1;
  for (c = start; c != end; c++) {
    if (((*c >= 'A' && *c <= 'Z') || (*c >= 'a' && *c <= 'z') || (*c >= '0' && *c <= '9') ||
         ((c != start) && (c != last) && (*c == '_'))) == 0) {
      BKE_reportf(reports, report_level, "'%s' doesn't have an alpha-numeric suffix", identifier);
      return failure;
    }
  }
  return true;
}

/* Property Information */

const char *RNA_property_identifier(const PropertyRNA *prop)
{
  return rna_ensure_property_identifier(prop);
}

const char *RNA_property_description(PropertyRNA *prop)
{
  return TIP_(rna_ensure_property_description(prop));
}

PropertyType RNA_property_type(PropertyRNA *prop)
{
  return rna_ensure_property(prop)->type;
}

PropertySubType RNA_property_subtype(PropertyRNA *prop)
{
  PropertyRNA *rna_prop = rna_ensure_property(prop);

  /* For custom properties, find and parse the 'subtype' metadata field. */
  if (prop->magic != RNA_MAGIC) {
    IDProperty *idprop = (IDProperty *)prop;

    /* Restrict to arrays only for now for performance reasons. */
    if (idprop->type == IDP_ARRAY && ELEM(idprop->subtype, IDP_INT, IDP_FLOAT, IDP_DOUBLE)) {
      IDProperty *idp_ui = rna_idproperty_ui(prop);

      if (idp_ui) {
        IDProperty *item = IDP_GetPropertyTypeFromGroup(idp_ui, "subtype", IDP_STRING);

        if (item) {
          int result = PROP_NONE;
          RNA_enum_value_from_id(rna_enum_property_subtype_items, IDP_String(item), &result);
          return (PropertySubType)result;
        }
      }
    }
  }

  return rna_prop->subtype;
}

PropertyUnit RNA_property_unit(PropertyRNA *prop)
{
  return RNA_SUBTYPE_UNIT(RNA_property_subtype(prop));
}

int RNA_property_flag(PropertyRNA *prop)
{
  return rna_ensure_property(prop)->flag;
}

/**
 * Get the tags set for \a prop as int bitfield.
 * \note Doesn't perform any validity check on the set bits. #RNA_def_property_tags does this
 *       in debug builds (to avoid performance issues in non-debug builds), which should be
 *       the only way to set tags. Hence, at this point we assume the tag bitfield to be valid.
 */
int RNA_property_tags(PropertyRNA *prop)
{
  return rna_ensure_property(prop)->tags;
}

bool RNA_property_builtin(PropertyRNA *prop)
{
  return (rna_ensure_property(prop)->flag_internal & PROP_INTERN_BUILTIN) != 0;
}

void *RNA_property_py_data_get(PropertyRNA *prop)
{
  return prop->py_data;
}

int RNA_property_array_length(PointerRNA *ptr, PropertyRNA *prop)
{
  return rna_ensure_property_array_length(ptr, prop);
}

bool RNA_property_array_check(PropertyRNA *prop)
{
  return rna_ensure_property_array_check(prop);
}

/* used by BPY to make an array from the python object */
int RNA_property_array_dimension(PointerRNA *ptr, PropertyRNA *prop, int length[])
{
  PropertyRNA *rprop = rna_ensure_property(prop);

  if (length) {
    rna_ensure_property_multi_array_length(ptr, prop, length);
  }

  return rprop->arraydimension;
}

/* Return the size of Nth dimension. */
int RNA_property_multi_array_length(PointerRNA *ptr, PropertyRNA *prop, int dim)
{
  int len[RNA_MAX_ARRAY_DIMENSION];

  rna_ensure_property_multi_array_length(ptr, prop, len);

  return len[dim];
}

char RNA_property_array_item_char(PropertyRNA *prop, int index)
{
  const char *vectoritem = "XYZW";
  const char *quatitem = "WXYZ";
  const char *coloritem = "RGBA";
  PropertySubType subtype = RNA_property_subtype(prop);

  BLI_assert(index >= 0);

  /* get string to use for array index */
  if ((index < 4) && ELEM(subtype, PROP_QUATERNION, PROP_AXISANGLE)) {
    return quatitem[index];
  }
  else if ((index < 4) && ELEM(subtype,
                               PROP_TRANSLATION,
                               PROP_DIRECTION,
                               PROP_XYZ,
                               PROP_XYZ_LENGTH,
                               PROP_EULER,
                               PROP_VELOCITY,
                               PROP_ACCELERATION,
                               PROP_COORDS)) {
    return vectoritem[index];
  }
  else if ((index < 4) && ELEM(subtype, PROP_COLOR, PROP_COLOR_GAMMA)) {
    return coloritem[index];
  }

  return '\0';
}

int RNA_property_array_item_index(PropertyRNA *prop, char name)
{
  /* Don't use custom property subtypes in RNA path lookup. */
  PropertySubType subtype = rna_ensure_property(prop)->subtype;

  /* get index based on string name/alias */
  /* maybe a function to find char index in string would be better than all the switches */
  if (ELEM(subtype, PROP_QUATERNION, PROP_AXISANGLE)) {
    switch (name) {
      case 'w':
        return 0;
      case 'x':
        return 1;
      case 'y':
        return 2;
      case 'z':
        return 3;
    }
  }
  else if (ELEM(subtype,
                PROP_TRANSLATION,
                PROP_DIRECTION,
                PROP_XYZ,
                PROP_XYZ_LENGTH,
                PROP_EULER,
                PROP_VELOCITY,
                PROP_ACCELERATION)) {
    switch (name) {
      case 'x':
        return 0;
      case 'y':
        return 1;
      case 'z':
        return 2;
      case 'w':
        return 3;
    }
  }
  else if (ELEM(subtype, PROP_COLOR, PROP_COLOR_GAMMA)) {
    switch (name) {
      case 'r':
        return 0;
      case 'g':
        return 1;
      case 'b':
        return 2;
      case 'a':
        return 3;
    }
  }

  return -1;
}

void RNA_property_int_range(PointerRNA *ptr, PropertyRNA *prop, int *hardmin, int *hardmax)
{
  IntPropertyRNA *iprop = (IntPropertyRNA *)rna_ensure_property(prop);
  int softmin, softmax;

  if (prop->magic != RNA_MAGIC) {
    /* attempt to get the local ID values */
    IDProperty *idp_ui = rna_idproperty_ui(prop);

    if (idp_ui) {
      IDProperty *item;

      item = IDP_GetPropertyTypeFromGroup(idp_ui, "min", IDP_INT);
      *hardmin = item ? IDP_Int(item) : INT_MIN;

      item = IDP_GetPropertyTypeFromGroup(idp_ui, "max", IDP_INT);
      *hardmax = item ? IDP_Int(item) : INT_MAX;

      return;
    }
  }

  if (iprop->range) {
    *hardmin = INT_MIN;
    *hardmax = INT_MAX;

    iprop->range(ptr, hardmin, hardmax, &softmin, &softmax);
  }
  else if (iprop->range_ex) {
    *hardmin = INT_MIN;
    *hardmax = INT_MAX;

    iprop->range_ex(ptr, prop, hardmin, hardmax, &softmin, &softmax);
  }
  else {
    *hardmin = iprop->hardmin;
    *hardmax = iprop->hardmax;
  }
}

void RNA_property_int_ui_range(
    PointerRNA *ptr, PropertyRNA *prop, int *softmin, int *softmax, int *step)
{
  IntPropertyRNA *iprop = (IntPropertyRNA *)rna_ensure_property(prop);
  int hardmin, hardmax;

  if (prop->magic != RNA_MAGIC) {
    /* attempt to get the local ID values */
    IDProperty *idp_ui = rna_idproperty_ui(prop);

    if (idp_ui) {
      IDProperty *item;

      item = IDP_GetPropertyTypeFromGroup(idp_ui, "soft_min", IDP_INT);
      *softmin = item ? IDP_Int(item) : INT_MIN;

      item = IDP_GetPropertyTypeFromGroup(idp_ui, "soft_max", IDP_INT);
      *softmax = item ? IDP_Int(item) : INT_MAX;

      item = IDP_GetPropertyTypeFromGroup(idp_ui, "step", IDP_INT);
      *step = item ? IDP_Int(item) : 1;

      return;
    }
  }

  *softmin = iprop->softmin;
  *softmax = iprop->softmax;

  if (iprop->range) {
    hardmin = INT_MIN;
    hardmax = INT_MAX;

    iprop->range(ptr, &hardmin, &hardmax, softmin, softmax);

    *softmin = max_ii(*softmin, hardmin);
    *softmax = min_ii(*softmax, hardmax);
  }
  else if (iprop->range_ex) {
    hardmin = INT_MIN;
    hardmax = INT_MAX;

    iprop->range_ex(ptr, prop, &hardmin, &hardmax, softmin, softmax);

    *softmin = max_ii(*softmin, hardmin);
    *softmax = min_ii(*softmax, hardmax);
  }

  *step = iprop->step;
}

void RNA_property_float_range(PointerRNA *ptr, PropertyRNA *prop, float *hardmin, float *hardmax)
{
  FloatPropertyRNA *fprop = (FloatPropertyRNA *)rna_ensure_property(prop);
  float softmin, softmax;

  if (prop->magic != RNA_MAGIC) {
    /* attempt to get the local ID values */
    IDProperty *idp_ui = rna_idproperty_ui(prop);

    if (idp_ui) {
      IDProperty *item;

      item = IDP_GetPropertyTypeFromGroup(idp_ui, "min", IDP_DOUBLE);
      *hardmin = item ? (float)IDP_Double(item) : -FLT_MAX;

      item = IDP_GetPropertyTypeFromGroup(idp_ui, "max", IDP_DOUBLE);
      *hardmax = item ? (float)IDP_Double(item) : FLT_MAX;

      return;
    }
  }

  if (fprop->range) {
    *hardmin = -FLT_MAX;
    *hardmax = FLT_MAX;

    fprop->range(ptr, hardmin, hardmax, &softmin, &softmax);
  }
  else if (fprop->range_ex) {
    *hardmin = -FLT_MAX;
    *hardmax = FLT_MAX;

    fprop->range_ex(ptr, prop, hardmin, hardmax, &softmin, &softmax);
  }
  else {
    *hardmin = fprop->hardmin;
    *hardmax = fprop->hardmax;
  }
}

void RNA_property_float_ui_range(PointerRNA *ptr,
                                 PropertyRNA *prop,
                                 float *softmin,
                                 float *softmax,
                                 float *step,
                                 float *precision)
{
  FloatPropertyRNA *fprop = (FloatPropertyRNA *)rna_ensure_property(prop);
  float hardmin, hardmax;

  if (prop->magic != RNA_MAGIC) {
    /* attempt to get the local ID values */
    IDProperty *idp_ui = rna_idproperty_ui(prop);

    if (idp_ui) {
      IDProperty *item;

      item = IDP_GetPropertyTypeFromGroup(idp_ui, "soft_min", IDP_DOUBLE);
      *softmin = item ? (float)IDP_Double(item) : -FLT_MAX;

      item = IDP_GetPropertyTypeFromGroup(idp_ui, "soft_max", IDP_DOUBLE);
      *softmax = item ? (float)IDP_Double(item) : FLT_MAX;

      item = IDP_GetPropertyTypeFromGroup(idp_ui, "step", IDP_DOUBLE);
      *step = item ? (float)IDP_Double(item) : 1.0f;

      item = IDP_GetPropertyTypeFromGroup(idp_ui, "precision", IDP_DOUBLE);
      *precision = item ? (float)IDP_Double(item) : 3.0f;

      return;
    }
  }

  *softmin = fprop->softmin;
  *softmax = fprop->softmax;

  if (fprop->range) {
    hardmin = -FLT_MAX;
    hardmax = FLT_MAX;

    fprop->range(ptr, &hardmin, &hardmax, softmin, softmax);

    *softmin = max_ff(*softmin, hardmin);
    *softmax = min_ff(*softmax, hardmax);
  }
  else if (fprop->range_ex) {
    hardmin = -FLT_MAX;
    hardmax = FLT_MAX;

    fprop->range_ex(ptr, prop, &hardmin, &hardmax, softmin, softmax);

    *softmin = max_ff(*softmin, hardmin);
    *softmax = min_ff(*softmax, hardmax);
  }

  *step = fprop->step;
  *precision = (float)fprop->precision;
}

int RNA_property_float_clamp(PointerRNA *ptr, PropertyRNA *prop, float *value)
{
  float min, max;

  RNA_property_float_range(ptr, prop, &min, &max);

  if (*value < min) {
    *value = min;
    return -1;
  }
  else if (*value > max) {
    *value = max;
    return 1;
  }
  else {
    return 0;
  }
}

int RNA_property_int_clamp(PointerRNA *ptr, PropertyRNA *prop, int *value)
{
  int min, max;

  RNA_property_int_range(ptr, prop, &min, &max);

  if (*value < min) {
    *value = min;
    return -1;
  }
  else if (*value > max) {
    *value = max;
    return 1;
  }
  else {
    return 0;
  }
}

/* this is the max length including \0 terminator.
 * '0' used when their is no maximum */
int RNA_property_string_maxlength(PropertyRNA *prop)
{
  StringPropertyRNA *sprop = (StringPropertyRNA *)rna_ensure_property(prop);
  return sprop->maxlength;
}

StructRNA *RNA_property_pointer_type(PointerRNA *ptr, PropertyRNA *prop)
{
  prop = rna_ensure_property(prop);

  if (prop->type == PROP_POINTER) {
    PointerPropertyRNA *pprop = (PointerPropertyRNA *)prop;

    if (pprop->typef) {
      return pprop->typef(ptr);
    }
    else if (pprop->type) {
      return pprop->type;
    }
  }
  else if (prop->type == PROP_COLLECTION) {
    CollectionPropertyRNA *cprop = (CollectionPropertyRNA *)prop;

    if (cprop->item_type) {
      return cprop->item_type;
    }
  }
  /* ignore other types, RNA_struct_find_nested calls with unchecked props */

  return &RNA_UnknownType;
}

bool RNA_property_pointer_poll(PointerRNA *ptr, PropertyRNA *prop, PointerRNA *value)
{
  prop = rna_ensure_property(prop);

  if (prop->type == PROP_POINTER) {
    PointerPropertyRNA *pprop = (PointerPropertyRNA *)prop;

    if (pprop->poll) {
      if (rna_idproperty_check(&prop, ptr)) {
        return ((PropPointerPollFuncPy)pprop->poll)(ptr, *value, prop);
      }
      else {
        return pprop->poll(ptr, *value);
      }
    }

    return 1;
  }

  printf("%s: %s is not a pointer property.\n", __func__, prop->identifier);
  return 0;
}

void RNA_property_enum_items_ex(bContext *C,
                                PointerRNA *ptr,
                                PropertyRNA *prop,
                                const bool use_static,
                                const EnumPropertyItem **r_item,
                                int *r_totitem,
                                bool *r_free)
{
  EnumPropertyRNA *eprop = (EnumPropertyRNA *)rna_ensure_property(prop);

  *r_free = false;

  if (!use_static && eprop->itemf && (C != NULL || (prop->flag & PROP_ENUM_NO_CONTEXT))) {
    const EnumPropertyItem *item;

    if (prop->flag & PROP_ENUM_NO_CONTEXT) {
      item = eprop->itemf(NULL, ptr, prop, r_free);
    }
    else {
      item = eprop->itemf(C, ptr, prop, r_free);
    }

    /* any callbacks returning NULL should be fixed */
    BLI_assert(item != NULL);

    if (r_totitem) {
      int tot;
      for (tot = 0; item[tot].identifier; tot++) {
        /* pass */
      }
      *r_totitem = tot;
    }

    *r_item = item;
  }
  else {
    *r_item = eprop->item;
    if (r_totitem) {
      *r_totitem = eprop->totitem;
    }
  }
}

void RNA_property_enum_items(bContext *C,
                             PointerRNA *ptr,
                             PropertyRNA *prop,
                             const EnumPropertyItem **r_item,
                             int *r_totitem,
                             bool *r_free)
{
  RNA_property_enum_items_ex(C, ptr, prop, false, r_item, r_totitem, r_free);
}

#ifdef WITH_INTERNATIONAL
static void property_enum_translate(PropertyRNA *prop,
                                    EnumPropertyItem **r_item,
                                    int *r_totitem,
                                    bool *r_free)
{
  if (!(prop->flag & PROP_ENUM_NO_TRANSLATE)) {
    int i;

    /* Note: Only do those tests once, and then use BLT_pgettext. */
    bool do_iface = BLT_translate_iface();
    bool do_tooltip = BLT_translate_tooltips();
    EnumPropertyItem *nitem;

    if (!(do_iface || do_tooltip)) {
      return;
    }

    if (*r_free) {
      nitem = *r_item;
    }
    else {
      const EnumPropertyItem *item = *r_item;
      int tot;

      if (r_totitem) {
        tot = *r_totitem;
      }
      else {
        /* count */
        for (tot = 0; item[tot].identifier; tot++) {
          /* pass */
        }
      }

      nitem = MEM_mallocN(sizeof(EnumPropertyItem) * (tot + 1), "enum_items_gettexted");
      memcpy(nitem, item, sizeof(EnumPropertyItem) * (tot + 1));

      *r_free = true;
    }

    for (i = 0; nitem[i].identifier; i++) {
      if (nitem[i].name && do_iface) {
        nitem[i].name = BLT_pgettext(prop->translation_context, nitem[i].name);
      }
      if (nitem[i].description && do_tooltip) {
        nitem[i].description = BLT_pgettext(NULL, nitem[i].description);
      }
    }

    *r_item = nitem;
  }
}
#endif

void RNA_property_enum_items_gettexted(bContext *C,
                                       PointerRNA *ptr,
                                       PropertyRNA *prop,
                                       const EnumPropertyItem **r_item,
                                       int *r_totitem,
                                       bool *r_free)
{
  RNA_property_enum_items(C, ptr, prop, r_item, r_totitem, r_free);

#ifdef WITH_INTERNATIONAL
  /* Normally dropping 'const' is _not_ ok, in this case it's only modified if we own the memory
   * so allow the exception (callers are creating new arrays in this case). */
  property_enum_translate(prop, (EnumPropertyItem **)r_item, r_totitem, r_free);
#endif
}

void RNA_property_enum_items_gettexted_all(bContext *C,
                                           PointerRNA *ptr,
                                           PropertyRNA *prop,
                                           const EnumPropertyItem **r_item,
                                           int *r_totitem,
                                           bool *r_free)
{
  EnumPropertyRNA *eprop = (EnumPropertyRNA *)rna_ensure_property(prop);
  int mem_size = sizeof(EnumPropertyItem) * (eprop->totitem + 1);
  /* first return all items */
  EnumPropertyItem *item_array = MEM_mallocN(mem_size, "enum_gettext_all");
  *r_free = true;
  memcpy(item_array, eprop->item, mem_size);

  if (r_totitem) {
    *r_totitem = eprop->totitem;
  }

  if (eprop->itemf && (C != NULL || (prop->flag & PROP_ENUM_NO_CONTEXT))) {
    const EnumPropertyItem *item;
    int i;
    bool free = false;

    if (prop->flag & PROP_ENUM_NO_CONTEXT) {
      item = eprop->itemf(NULL, ptr, prop, &free);
    }
    else {
      item = eprop->itemf(C, ptr, prop, &free);
    }

    /* any callbacks returning NULL should be fixed */
    BLI_assert(item != NULL);

    for (i = 0; i < eprop->totitem; i++) {
      bool exists = false;
      int i_fixed;

      /* Items that do not exist on list are returned,
       * but have their names/identifiers NULL'ed out. */
      for (i_fixed = 0; item[i_fixed].identifier; i_fixed++) {
        if (STREQ(item[i_fixed].identifier, item_array[i].identifier)) {
          exists = true;
          break;
        }
      }

      if (!exists) {
        item_array[i].name = NULL;
        item_array[i].identifier = "";
      }
    }

    if (free) {
      MEM_freeN((void *)item);
    }
  }

#ifdef WITH_INTERNATIONAL
  property_enum_translate(prop, &item_array, r_totitem, r_free);
#endif
  *r_item = item_array;
}

bool RNA_property_enum_value(
    bContext *C, PointerRNA *ptr, PropertyRNA *prop, const char *identifier, int *r_value)
{
  const EnumPropertyItem *item;
  bool free;
  bool found;

  RNA_property_enum_items(C, ptr, prop, &item, NULL, &free);

  if (item) {
    const int i = RNA_enum_from_identifier(item, identifier);
    if (i != -1) {
      *r_value = item[i].value;
      found = true;
    }
    else {
      found = false;
    }

    if (free) {
      MEM_freeN((void *)item);
    }
  }
  else {
    found = false;
  }
  return found;
}

bool RNA_enum_identifier(const EnumPropertyItem *item, const int value, const char **r_identifier)
{
  const int i = RNA_enum_from_value(item, value);
  if (i != -1) {
    *r_identifier = item[i].identifier;
    return true;
  }
  else {
    return false;
  }
}

int RNA_enum_bitflag_identifiers(const EnumPropertyItem *item,
                                 const int value,
                                 const char **r_identifier)
{
  int index = 0;
  for (; item->identifier; item++) {
    if (item->identifier[0] && item->value & value) {
      r_identifier[index++] = item->identifier;
    }
  }
  r_identifier[index] = NULL;
  return index;
}

bool RNA_enum_name(const EnumPropertyItem *item, const int value, const char **r_name)
{
  const int i = RNA_enum_from_value(item, value);
  if (i != -1) {
    *r_name = item[i].name;
    return true;
  }
  else {
    return false;
  }
}

bool RNA_enum_description(const EnumPropertyItem *item,
                          const int value,
                          const char **r_description)
{
  const int i = RNA_enum_from_value(item, value);
  if (i != -1) {
    *r_description = item[i].description;
    return true;
  }
  else {
    return false;
  }
}

int RNA_enum_from_identifier(const EnumPropertyItem *item, const char *identifier)
{
  int i = 0;
  for (; item->identifier; item++, i++) {
    if (item->identifier[0] && STREQ(item->identifier, identifier)) {
      return i;
    }
  }
  return -1;
}

/**
 * Take care using this with translated enums,
 * prefer #RNA_enum_from_identifier where possible.
 */
int RNA_enum_from_name(const EnumPropertyItem *item, const char *name)
{
  int i = 0;
  for (; item->identifier; item++, i++) {
    if (item->identifier[0] && STREQ(item->name, name)) {
      return i;
    }
  }
  return -1;
}

int RNA_enum_from_value(const EnumPropertyItem *item, const int value)
{
  int i = 0;
  for (; item->identifier; item++, i++) {
    if (item->identifier[0] && item->value == value) {
      return i;
    }
  }
  return -1;
}

unsigned int RNA_enum_items_count(const EnumPropertyItem *item)
{
  unsigned int i = 0;

  while (item->identifier) {
    item++;
    i++;
  }

  return i;
}

bool RNA_property_enum_identifier(
    bContext *C, PointerRNA *ptr, PropertyRNA *prop, const int value, const char **identifier)
{
  const EnumPropertyItem *item = NULL;
  bool free;

  RNA_property_enum_items(C, ptr, prop, &item, NULL, &free);
  if (item) {
    bool result;
    result = RNA_enum_identifier(item, value, identifier);
    if (free) {
      MEM_freeN((void *)item);
    }
    return result;
  }
  return false;
}

bool RNA_property_enum_name(
    bContext *C, PointerRNA *ptr, PropertyRNA *prop, const int value, const char **name)
{
  const EnumPropertyItem *item = NULL;
  bool free;

  RNA_property_enum_items(C, ptr, prop, &item, NULL, &free);
  if (item) {
    bool result;
    result = RNA_enum_name(item, value, name);
    if (free) {
      MEM_freeN((void *)item);
    }

    return result;
  }
  return false;
}

bool RNA_property_enum_name_gettexted(
    bContext *C, PointerRNA *ptr, PropertyRNA *prop, const int value, const char **name)
{
  bool result;

  result = RNA_property_enum_name(C, ptr, prop, value, name);

  if (result) {
    if (!(prop->flag & PROP_ENUM_NO_TRANSLATE)) {
      if (BLT_translate_iface()) {
        *name = BLT_pgettext(prop->translation_context, *name);
      }
    }
  }

  return result;
}

bool RNA_property_enum_item_from_value(
    bContext *C, PointerRNA *ptr, PropertyRNA *prop, const int value, EnumPropertyItem *r_item)
{
  const EnumPropertyItem *item = NULL;
  bool free;

  RNA_property_enum_items(C, ptr, prop, &item, NULL, &free);
  if (item) {
    const int i = RNA_enum_from_value(item, value);
    bool result;

    if (i != -1) {
      *r_item = item[i];
      result = true;
    }
    else {
      result = false;
    }

    if (free) {
      MEM_freeN((void *)item);
    }

    return result;
  }
  return false;
}

bool RNA_property_enum_item_from_value_gettexted(
    bContext *C, PointerRNA *ptr, PropertyRNA *prop, const int value, EnumPropertyItem *r_item)
{
  bool result;

  result = RNA_property_enum_item_from_value(C, ptr, prop, value, r_item);

  if (!(prop->flag & PROP_ENUM_NO_TRANSLATE)) {
    if (BLT_translate_iface()) {
      r_item->name = BLT_pgettext(prop->translation_context, r_item->name);
    }
  }

  return result;
}

int RNA_property_enum_bitflag_identifiers(
    bContext *C, PointerRNA *ptr, PropertyRNA *prop, const int value, const char **identifier)
{
  const EnumPropertyItem *item = NULL;
  bool free;

  RNA_property_enum_items(C, ptr, prop, &item, NULL, &free);
  if (item) {
    int result;
    result = RNA_enum_bitflag_identifiers(item, value, identifier);
    if (free) {
      MEM_freeN((void *)item);
    }

    return result;
  }
  return 0;
}

const char *RNA_property_ui_name(PropertyRNA *prop)
{
  return CTX_IFACE_(prop->translation_context, rna_ensure_property_name(prop));
}

const char *RNA_property_ui_name_raw(PropertyRNA *prop)
{
  return rna_ensure_property_name(prop);
}

const char *RNA_property_ui_description(PropertyRNA *prop)
{
  return TIP_(rna_ensure_property_description(prop));
}

const char *RNA_property_ui_description_raw(PropertyRNA *prop)
{
  return rna_ensure_property_description(prop);
}

const char *RNA_property_translation_context(PropertyRNA *_prop)
{
  PropertyRNA *prop = rna_ensure_property(_prop);
  return prop->translation_context;
}

int RNA_property_ui_icon(PropertyRNA *prop)
{
  return rna_ensure_property(prop)->icon;
}

bool RNA_property_editable(PointerRNA *ptr, PropertyRNA *prop_orig)
{
  ID *id = ptr->owner_id;
  int flag;
  const char *dummy_info;

  PropertyRNA *prop = rna_ensure_property(prop_orig);
  flag = prop->editable ? prop->editable(ptr, &dummy_info) : prop->flag;

  return ((flag & PROP_EDITABLE) && (flag & PROP_REGISTER) == 0 &&
          (!id || ((!ID_IS_LINKED(id) || (prop->flag & PROP_LIB_EXCEPTION)) &&
                   (!id->override_library || RNA_property_overridable_get(ptr, prop_orig)))));
}

/**
 * Version of #RNA_property_editable that tries to return additional info in \a r_info
 * that can be exposed in UI.
 */
bool RNA_property_editable_info(PointerRNA *ptr, PropertyRNA *prop, const char **r_info)
{
  ID *id = ptr->owner_id;
  int flag;

  PropertyRNA *prop_type = rna_ensure_property(prop);
  *r_info = "";

  /* get flag */
  if (prop_type->editable) {
    flag = prop_type->editable(ptr, r_info);
  }
  else {
    flag = prop_type->flag;
    if ((flag & PROP_EDITABLE) == 0 || (flag & PROP_REGISTER)) {
      *r_info = N_("This property is for internal use only and can't be edited");
    }
  }

  /* property from linked data-block */
  if (id) {
    if (ID_IS_LINKED(id) && (prop_type->flag & PROP_LIB_EXCEPTION) == 0) {
      if (!(*r_info)[0]) {
        *r_info = N_("Can't edit this property from a linked data-block");
      }
      return false;
    }
    if (id->override_library != NULL) {
      /* We need the real data property in case of IDProperty here... */
      PropertyRNA *real_prop = rna_ensure_property_realdata(&prop, ptr);
      if (real_prop == NULL || !RNA_property_overridable_get(ptr, real_prop)) {
        if (!(*r_info)[0]) {
          *r_info = N_("Can't edit this property from an override data-block");
        }
        return false;
      }
    }
  }

  return ((flag & PROP_EDITABLE) && (flag & PROP_REGISTER) == 0);
}

bool RNA_property_editable_flag(PointerRNA *ptr, PropertyRNA *prop)
{
  int flag;
  const char *dummy_info;

  prop = rna_ensure_property(prop);
  flag = prop->editable ? prop->editable(ptr, &dummy_info) : prop->flag;
  return (flag & PROP_EDITABLE) != 0;
}

/* same as RNA_property_editable(), except this checks individual items in an array */
bool RNA_property_editable_index(PointerRNA *ptr, PropertyRNA *prop, int index)
{
  ID *id;
  int flag;

  BLI_assert(index >= 0);

  prop = rna_ensure_property(prop);

  flag = prop->flag;

  if (prop->editable) {
    const char *dummy_info;
    flag &= prop->editable(ptr, &dummy_info);
  }

  if (prop->itemeditable) {
    flag &= prop->itemeditable(ptr, index);
  }

  id = ptr->owner_id;

  return (flag & PROP_EDITABLE) && (!id || !ID_IS_LINKED(id) || (prop->flag & PROP_LIB_EXCEPTION));
}

bool RNA_property_animateable(PointerRNA *ptr, PropertyRNA *prop)
{
  /* check that base ID-block can support animation data */
  if (!id_can_have_animdata(ptr->owner_id)) {
    return false;
  }

  prop = rna_ensure_property(prop);

  if (!(prop->flag & PROP_ANIMATABLE)) {
    return false;
  }

  return (prop->flag & PROP_EDITABLE) != 0;
}

bool RNA_property_animated(PointerRNA *ptr, PropertyRNA *prop)
{
  int len = 1, index;
  bool driven, special;

  if (!prop) {
    return false;
  }

  if (RNA_property_array_check(prop)) {
    len = RNA_property_array_length(ptr, prop);
  }

  for (index = 0; index < len; index++) {
    if (rna_get_fcurve(ptr, prop, index, NULL, NULL, &driven, &special)) {
      return true;
    }
  }

  return false;
}
/* this function is to check if its possible to create a valid path from the ID
 * its slow so don't call in a loop */
bool RNA_property_path_from_ID_check(PointerRNA *ptr, PropertyRNA *prop)
{
  char *path = RNA_path_from_ID_to_property(ptr, prop);
  bool ret = false;

  if (path) {
    PointerRNA id_ptr;
    PointerRNA r_ptr;
    PropertyRNA *r_prop;

    RNA_id_pointer_create(ptr->owner_id, &id_ptr);
    if (RNA_path_resolve(&id_ptr, path, &r_ptr, &r_prop) == true) {
      ret = (prop == r_prop);
    }
    MEM_freeN(path);
  }

  return ret;
}

static void rna_property_update(
    bContext *C, Main *bmain, Scene *scene, PointerRNA *ptr, PropertyRNA *prop)
{
  const bool is_rna = (prop->magic == RNA_MAGIC);
  prop = rna_ensure_property(prop);

  if (is_rna) {
    if (prop->update) {
      /* ideally no context would be needed for update, but there's some
       * parts of the code that need it still, so we have this exception */
      if (prop->flag & PROP_CONTEXT_UPDATE) {
        if (C) {
          if ((prop->flag & PROP_CONTEXT_PROPERTY_UPDATE) == PROP_CONTEXT_PROPERTY_UPDATE) {
            ((ContextPropUpdateFunc)prop->update)(C, ptr, prop);
          }
          else {
            ((ContextUpdateFunc)prop->update)(C, ptr);
          }
        }
      }
      else {
        prop->update(bmain, scene, ptr);
      }
    }

#if 1
    /* TODO(campbell): Should eventually be replaced entirely by message bus (below)
     * for now keep since COW, bugs are hard to track when we have other missing updates. */
    if (prop->noteflag) {
      WM_main_add_notifier(prop->noteflag, ptr->owner_id);
    }
#endif

    /* if C is NULL, we're updating from animation.
     * avoid slow-down from f-curves by not publishing (for now). */
    if (C != NULL) {
      struct wmMsgBus *mbus = CTX_wm_message_bus(C);
      /* we could add NULL check, for now don't */
      WM_msg_publish_rna(mbus, ptr, prop);
    }
    if (ptr->owner_id != NULL && ((prop->flag & PROP_NO_DEG_UPDATE) == 0)) {
      const short id_type = GS(ptr->owner_id->name);
      if (ID_TYPE_IS_COW(id_type)) {
        DEG_id_tag_update(ptr->owner_id, ID_RECALC_COPY_ON_WRITE);
      }
    }
    /* End message bus. */
  }

  if (!is_rna || (prop->flag & PROP_IDPROPERTY)) {

    /* Disclaimer: this logic is not applied consistently, causing some confusing behavior.
     *
     * - When animated (which skips update functions).
     * - When ID-properties are edited via Python (since RNA properties aren't used in this case).
     *
     * Adding updates will add a lot of overhead in the case of animation.
     * For Python it may cause unexpected slow-downs for developers using ID-properties
     * for data storage. Further, the root ID isn't available with nested data-structures.
     *
     * So editing custom properties only causes updates in the UI,
     * keep this exception because it happens to be useful for driving settings.
     * Python developers on the other hand will need to manually 'update_tag', see: T74000. */
    DEG_id_tag_update(ptr->owner_id,
                      ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_PARAMETERS);

    WM_main_add_notifier(NC_WINDOW, NULL);
    /* Not nice as well, but the only way to make sure material preview
     * is updated with custom nodes.
     */
    if ((prop->flag & PROP_IDPROPERTY) != 0 && (ptr->owner_id != NULL) &&
        (GS(ptr->owner_id->name) == ID_NT)) {
      WM_main_add_notifier(NC_MATERIAL | ND_SHADING, NULL);
    }
  }
}

/* must keep in sync with 'rna_property_update'
 * note, its possible this returns a false positive in the case of PROP_CONTEXT_UPDATE
 * but this isn't likely to be a performance problem. */
bool RNA_property_update_check(PropertyRNA *prop)
{
  return (prop->magic != RNA_MAGIC || prop->update || prop->noteflag);
}

void RNA_property_update(bContext *C, PointerRNA *ptr, PropertyRNA *prop)
{
  rna_property_update(C, CTX_data_main(C), CTX_data_scene(C), ptr, prop);
}

void RNA_property_update_main(Main *bmain, Scene *scene, PointerRNA *ptr, PropertyRNA *prop)
{
  rna_property_update(NULL, bmain, scene, ptr, prop);
}

/* RNA Updates Cache ------------------------ */
/* Overview of RNA Update cache system:
 *
 * RNA Update calls need to be cached in order to maintain reasonable performance
 * of the animation system (i.e. maintaining a somewhat interactive framerate)
 * while still allowing updates to be called (necessary in particular for modifier
 * property updates to actually work).
 *
 * The cache is structured with a dual-layer structure
 * - L1 = PointerRNA used as key; owner_id is used (it should always be defined,
 *        and most updates end up using just that anyways)
 * - L2 = Update functions to be called on those PointerRNA's
 */

/* cache element */
typedef struct tRnaUpdateCacheElem {
  struct tRnaUpdateCacheElem *next, *prev;

  PointerRNA ptr;   /* L1 key - id as primary, data secondary/ignored? */
  ListBase L2Funcs; /* L2 functions (LinkData<RnaUpdateFuncRef>) */
} tRnaUpdateCacheElem;

/* cache global (tRnaUpdateCacheElem's) - only accessible using these API calls */
static ListBase rna_updates_cache = {NULL, NULL};

/* ........................... */

void RNA_property_update_cache_add(PointerRNA *ptr, PropertyRNA *prop)
{
  const bool is_rna = (prop->magic == RNA_MAGIC);
  tRnaUpdateCacheElem *uce = NULL;
  UpdateFunc fn = NULL;
  LinkData *ld;

  /* sanity check */
  if (NULL == ptr) {
    return;
  }

  prop = rna_ensure_property(prop);

  /* we can only handle update calls with no context args for now (makes animsys updates easier) */
  if ((is_rna == false) || (prop->update == NULL) || (prop->flag & PROP_CONTEXT_UPDATE)) {
    return;
  }
  fn = prop->update;

  /* find cache element for which key matches... */
  for (uce = rna_updates_cache.first; uce; uce = uce->next) {
    /* Just match by id only for now,
     * since most update calls that we'll encounter only really care about this. */
    /* TODO: later, the cache might need to have some nesting on L1 to cope better
     * with these problems + some tagging to indicate we need this */
    if (uce->ptr.owner_id == ptr->owner_id) {
      break;
    }
  }
  if (uce == NULL) {
    /* create new instance */
    uce = MEM_callocN(sizeof(tRnaUpdateCacheElem), "tRnaUpdateCacheElem");
    BLI_addtail(&rna_updates_cache, uce);

    /* copy pointer */
    RNA_pointer_create(ptr->owner_id, ptr->type, ptr->data, &uce->ptr);
  }

  /* check on the update func */
  for (ld = uce->L2Funcs.first; ld; ld = ld->next) {
    /* stop on match - function already cached */
    if (fn == ld->data) {
      return;
    }
  }
  /* else... if still here, we need to add it */
  BLI_addtail(&uce->L2Funcs, BLI_genericNodeN(fn));
}

void RNA_property_update_cache_flush(Main *bmain, Scene *scene)
{
  tRnaUpdateCacheElem *uce;

  /* TODO: should we check that bmain and scene are valid? The above stuff doesn't! */

  /* execute the cached updates */
  for (uce = rna_updates_cache.first; uce; uce = uce->next) {
    LinkData *ld;

    for (ld = uce->L2Funcs.first; ld; ld = ld->next) {
      UpdateFunc fn = (UpdateFunc)ld->data;
      fn(bmain, scene, &uce->ptr);
    }
  }
}

void RNA_property_update_cache_free(void)
{
  tRnaUpdateCacheElem *uce, *ucn;

  for (uce = rna_updates_cache.first; uce; uce = ucn) {
    ucn = uce->next;

    /* free L2 cache */
    BLI_freelistN(&uce->L2Funcs);

    /* remove self */
    BLI_freelinkN(&rna_updates_cache, uce);
  }
}

/* ---------------------------------------------------------------------- */

/* Property Data */

bool RNA_property_boolean_get(PointerRNA *ptr, PropertyRNA *prop)
{
  BoolPropertyRNA *bprop = (BoolPropertyRNA *)prop;
  IDProperty *idprop;
  bool value;

  BLI_assert(RNA_property_type(prop) == PROP_BOOLEAN);
  BLI_assert(RNA_property_array_check(prop) == false);

  if ((idprop = rna_idproperty_check(&prop, ptr))) {
    value = IDP_Int(idprop) != 0;
  }
  else if (bprop->get) {
    value = bprop->get(ptr);
  }
  else if (bprop->get_ex) {
    value = bprop->get_ex(ptr, prop);
  }
  else {
    value = bprop->defaultvalue;
  }

  BLI_assert(ELEM(value, false, true));

  return value;
}

void RNA_property_boolean_set(PointerRNA *ptr, PropertyRNA *prop, bool value)
{
  BoolPropertyRNA *bprop = (BoolPropertyRNA *)prop;
  IDProperty *idprop;

  BLI_assert(RNA_property_type(prop) == PROP_BOOLEAN);
  BLI_assert(RNA_property_array_check(prop) == false);
  BLI_assert(ELEM(value, false, true));

  /* just in case other values are passed */
  BLI_assert(ELEM(value, true, false));

  if ((idprop = rna_idproperty_check(&prop, ptr))) {
    IDP_Int(idprop) = (int)value;
    rna_idproperty_touch(idprop);
  }
  else if (bprop->set) {
    bprop->set(ptr, value);
  }
  else if (bprop->set_ex) {
    bprop->set_ex(ptr, prop, value);
  }
  else if (prop->flag & PROP_EDITABLE) {
    IDPropertyTemplate val = {0};
    IDProperty *group;

    val.i = value;

    group = RNA_struct_idprops(ptr, 1);
    if (group) {
      IDP_AddToGroup(group, IDP_New(IDP_INT, &val, prop->identifier));
    }
  }
}

static void rna_property_boolean_fill_default_array_values(
    const bool *defarr, int defarr_length, bool defvalue, int out_length, bool *r_values)
{
  if (defarr && defarr_length > 0) {
    defarr_length = MIN2(defarr_length, out_length);
    memcpy(r_values, defarr, sizeof(bool) * defarr_length);
  }
  else {
    defarr_length = 0;
  }

  for (int i = defarr_length; i < out_length; i++) {
    r_values[i] = defvalue;
  }
}

static void rna_property_boolean_get_default_array_values(PointerRNA *ptr,
                                                          BoolPropertyRNA *bprop,
                                                          bool *r_values)
{
  int length = bprop->property.totarraylength;
  int out_length = RNA_property_array_length(ptr, (PropertyRNA *)bprop);

  rna_property_boolean_fill_default_array_values(
      bprop->defaultarray, length, bprop->defaultvalue, out_length, r_values);
}

void RNA_property_boolean_get_array(PointerRNA *ptr, PropertyRNA *prop, bool *values)
{
  BoolPropertyRNA *bprop = (BoolPropertyRNA *)prop;
  IDProperty *idprop;

  BLI_assert(RNA_property_type(prop) == PROP_BOOLEAN);
  BLI_assert(RNA_property_array_check(prop) != false);

  if ((idprop = rna_idproperty_check(&prop, ptr))) {
    if (prop->arraydimension == 0) {
      values[0] = RNA_property_boolean_get(ptr, prop);
    }
    else {
      int *values_src = IDP_Array(idprop);
      for (uint i = 0; i < idprop->len; i++) {
        values[i] = (bool)values_src[i];
      }
    }
  }
  else if (prop->arraydimension == 0) {
    values[0] = RNA_property_boolean_get(ptr, prop);
  }
  else if (bprop->getarray) {
    bprop->getarray(ptr, values);
  }
  else if (bprop->getarray_ex) {
    bprop->getarray_ex(ptr, prop, values);
  }
  else {
    rna_property_boolean_get_default_array_values(ptr, bprop, values);
  }
}

bool RNA_property_boolean_get_index(PointerRNA *ptr, PropertyRNA *prop, int index)
{
  bool tmp[RNA_MAX_ARRAY_LENGTH];
  int len = rna_ensure_property_array_length(ptr, prop);
  bool value;

  BLI_assert(RNA_property_type(prop) == PROP_BOOLEAN);
  BLI_assert(RNA_property_array_check(prop) != false);
  BLI_assert(index >= 0);
  BLI_assert(index < len);

  if (len <= RNA_MAX_ARRAY_LENGTH) {
    RNA_property_boolean_get_array(ptr, prop, tmp);
    value = tmp[index];
  }
  else {
    bool *tmparray;

    tmparray = MEM_mallocN(sizeof(bool) * len, __func__);
    RNA_property_boolean_get_array(ptr, prop, tmparray);
    value = tmparray[index];
    MEM_freeN(tmparray);
  }

  BLI_assert(ELEM(value, false, true));

  return value;
}

void RNA_property_boolean_set_array(PointerRNA *ptr, PropertyRNA *prop, const bool *values)
{
  BoolPropertyRNA *bprop = (BoolPropertyRNA *)prop;
  IDProperty *idprop;

  BLI_assert(RNA_property_type(prop) == PROP_BOOLEAN);
  BLI_assert(RNA_property_array_check(prop) != false);

  if ((idprop = rna_idproperty_check(&prop, ptr))) {
    if (prop->arraydimension == 0) {
      IDP_Int(idprop) = values[0];
    }
    else {
      int *values_dst = IDP_Array(idprop);
      for (uint i = 0; i < idprop->len; i++) {
        values_dst[i] = (int)values[i];
      }
    }
    rna_idproperty_touch(idprop);
  }
  else if (prop->arraydimension == 0) {
    RNA_property_boolean_set(ptr, prop, values[0]);
  }
  else if (bprop->setarray) {
    bprop->setarray(ptr, values);
  }
  else if (bprop->setarray_ex) {
    bprop->setarray_ex(ptr, prop, values);
  }
  else if (prop->flag & PROP_EDITABLE) {
    IDPropertyTemplate val = {0};
    IDProperty *group;

    val.array.len = prop->totarraylength;
    val.array.type = IDP_INT;

    group = RNA_struct_idprops(ptr, 1);
    if (group) {
      idprop = IDP_New(IDP_ARRAY, &val, prop->identifier);
      IDP_AddToGroup(group, idprop);
      int *values_dst = IDP_Array(idprop);
      for (uint i = 0; i < idprop->len; i++) {
        values_dst[i] = (int)values[i];
      }
    }
  }
}

void RNA_property_boolean_set_index(PointerRNA *ptr, PropertyRNA *prop, int index, bool value)
{
  bool tmp[RNA_MAX_ARRAY_LENGTH];
  int len = rna_ensure_property_array_length(ptr, prop);

  BLI_assert(RNA_property_type(prop) == PROP_BOOLEAN);
  BLI_assert(RNA_property_array_check(prop) != false);
  BLI_assert(index >= 0);
  BLI_assert(index < len);
  BLI_assert(ELEM(value, false, true));

  if (len <= RNA_MAX_ARRAY_LENGTH) {
    RNA_property_boolean_get_array(ptr, prop, tmp);
    tmp[index] = value;
    RNA_property_boolean_set_array(ptr, prop, tmp);
  }
  else {
    bool *tmparray;

    tmparray = MEM_mallocN(sizeof(bool) * len, __func__);
    RNA_property_boolean_get_array(ptr, prop, tmparray);
    tmparray[index] = value;
    RNA_property_boolean_set_array(ptr, prop, tmparray);
    MEM_freeN(tmparray);
  }
}

bool RNA_property_boolean_get_default(PointerRNA *UNUSED(ptr), PropertyRNA *prop)
{
  BoolPropertyRNA *bprop = (BoolPropertyRNA *)rna_ensure_property(prop);

  BLI_assert(RNA_property_type(prop) == PROP_BOOLEAN);
  BLI_assert(RNA_property_array_check(prop) == false);
  BLI_assert(ELEM(bprop->defaultvalue, false, true));

  return bprop->defaultvalue;
}

void RNA_property_boolean_get_default_array(PointerRNA *ptr, PropertyRNA *prop, bool *values)
{
  BoolPropertyRNA *bprop = (BoolPropertyRNA *)rna_ensure_property(prop);

  BLI_assert(RNA_property_type(prop) == PROP_BOOLEAN);
  BLI_assert(RNA_property_array_check(prop) != false);

  if (prop->arraydimension == 0) {
    values[0] = bprop->defaultvalue;
  }
  else {
    rna_property_boolean_get_default_array_values(ptr, bprop, values);
  }
}

bool RNA_property_boolean_get_default_index(PointerRNA *ptr, PropertyRNA *prop, int index)
{
  bool tmp[RNA_MAX_ARRAY_LENGTH];
  int len = rna_ensure_property_array_length(ptr, prop);

  BLI_assert(RNA_property_type(prop) == PROP_BOOLEAN);
  BLI_assert(RNA_property_array_check(prop) != false);
  BLI_assert(index >= 0);
  BLI_assert(index < len);

  if (len <= RNA_MAX_ARRAY_LENGTH) {
    RNA_property_boolean_get_default_array(ptr, prop, tmp);
    return tmp[index];
  }
  else {
    bool *tmparray, value;

    tmparray = MEM_mallocN(sizeof(bool) * len, __func__);
    RNA_property_boolean_get_default_array(ptr, prop, tmparray);
    value = tmparray[index];
    MEM_freeN(tmparray);

    return value;
  }
}

int RNA_property_int_get(PointerRNA *ptr, PropertyRNA *prop)
{
  IntPropertyRNA *iprop = (IntPropertyRNA *)prop;
  IDProperty *idprop;

  BLI_assert(RNA_property_type(prop) == PROP_INT);
  BLI_assert(RNA_property_array_check(prop) == false);

  if ((idprop = rna_idproperty_check(&prop, ptr))) {
    return IDP_Int(idprop);
  }
  else if (iprop->get) {
    return iprop->get(ptr);
  }
  else if (iprop->get_ex) {
    return iprop->get_ex(ptr, prop);
  }
  else {
    return iprop->defaultvalue;
  }
}

void RNA_property_int_set(PointerRNA *ptr, PropertyRNA *prop, int value)
{
  IntPropertyRNA *iprop = (IntPropertyRNA *)prop;
  IDProperty *idprop;

  BLI_assert(RNA_property_type(prop) == PROP_INT);
  BLI_assert(RNA_property_array_check(prop) == false);
  /* useful to check on bad values but set function should clamp */
  /* BLI_assert(RNA_property_int_clamp(ptr, prop, &value) == 0); */

  if ((idprop = rna_idproperty_check(&prop, ptr))) {
    RNA_property_int_clamp(ptr, prop, &value);
    IDP_Int(idprop) = value;
    rna_idproperty_touch(idprop);
  }
  else if (iprop->set) {
    iprop->set(ptr, value);
  }
  else if (iprop->set_ex) {
    iprop->set_ex(ptr, prop, value);
  }
  else if (prop->flag & PROP_EDITABLE) {
    IDPropertyTemplate val = {0};
    IDProperty *group;

    RNA_property_int_clamp(ptr, prop, &value);

    val.i = value;

    group = RNA_struct_idprops(ptr, 1);
    if (group) {
      IDP_AddToGroup(group, IDP_New(IDP_INT, &val, prop->identifier));
    }
  }
}

static void rna_property_int_fill_default_array_values(
    const int *defarr, int defarr_length, int defvalue, int out_length, int *r_values)
{
  if (defarr && defarr_length > 0) {
    defarr_length = MIN2(defarr_length, out_length);
    memcpy(r_values, defarr, sizeof(int) * defarr_length);
  }
  else {
    defarr_length = 0;
  }

  for (int i = defarr_length; i < out_length; i++) {
    r_values[i] = defvalue;
  }
}

static void rna_property_int_get_default_array_values(PointerRNA *ptr,
                                                      IntPropertyRNA *iprop,
                                                      int *r_values)
{
  int length = iprop->property.totarraylength;
  int out_length = RNA_property_array_length(ptr, (PropertyRNA *)iprop);

  rna_property_int_fill_default_array_values(
      iprop->defaultarray, length, iprop->defaultvalue, out_length, r_values);
}

void RNA_property_int_get_array(PointerRNA *ptr, PropertyRNA *prop, int *values)
{
  IntPropertyRNA *iprop = (IntPropertyRNA *)prop;
  IDProperty *idprop;

  BLI_assert(RNA_property_type(prop) == PROP_INT);
  BLI_assert(RNA_property_array_check(prop) != false);

  if ((idprop = rna_idproperty_check(&prop, ptr))) {
    BLI_assert(idprop->len == RNA_property_array_length(ptr, prop) ||
               (prop->flag & PROP_IDPROPERTY));
    if (prop->arraydimension == 0) {
      values[0] = RNA_property_int_get(ptr, prop);
    }
    else {
      memcpy(values, IDP_Array(idprop), sizeof(int) * idprop->len);
    }
  }
  else if (prop->arraydimension == 0) {
    values[0] = RNA_property_int_get(ptr, prop);
  }
  else if (iprop->getarray) {
    iprop->getarray(ptr, values);
  }
  else if (iprop->getarray_ex) {
    iprop->getarray_ex(ptr, prop, values);
  }
  else {
    rna_property_int_get_default_array_values(ptr, iprop, values);
  }
}

void RNA_property_int_get_array_range(PointerRNA *ptr, PropertyRNA *prop, int values[2])
{
  const int array_len = RNA_property_array_length(ptr, prop);

  if (array_len <= 0) {
    values[0] = 0;
    values[1] = 0;
  }
  else if (array_len == 1) {
    RNA_property_int_get_array(ptr, prop, values);
    values[1] = values[0];
  }
  else {
    int arr_stack[32];
    int *arr;
    int i;

    if (array_len > 32) {
      arr = MEM_mallocN(sizeof(int) * array_len, __func__);
    }
    else {
      arr = arr_stack;
    }

    RNA_property_int_get_array(ptr, prop, arr);
    values[0] = values[1] = arr[0];
    for (i = 1; i < array_len; i++) {
      values[0] = MIN2(values[0], arr[i]);
      values[1] = MAX2(values[1], arr[i]);
    }

    if (arr != arr_stack) {
      MEM_freeN(arr);
    }
  }
}

int RNA_property_int_get_index(PointerRNA *ptr, PropertyRNA *prop, int index)
{
  int tmp[RNA_MAX_ARRAY_LENGTH];
  int len = rna_ensure_property_array_length(ptr, prop);

  BLI_assert(RNA_property_type(prop) == PROP_INT);
  BLI_assert(RNA_property_array_check(prop) != false);
  BLI_assert(index >= 0);
  BLI_assert(index < len);

  if (len <= RNA_MAX_ARRAY_LENGTH) {
    RNA_property_int_get_array(ptr, prop, tmp);
    return tmp[index];
  }
  else {
    int *tmparray, value;

    tmparray = MEM_mallocN(sizeof(int) * len, __func__);
    RNA_property_int_get_array(ptr, prop, tmparray);
    value = tmparray[index];
    MEM_freeN(tmparray);

    return value;
  }
}

void RNA_property_int_set_array(PointerRNA *ptr, PropertyRNA *prop, const int *values)
{
  IntPropertyRNA *iprop = (IntPropertyRNA *)prop;
  IDProperty *idprop;

  BLI_assert(RNA_property_type(prop) == PROP_INT);
  BLI_assert(RNA_property_array_check(prop) != false);

  if ((idprop = rna_idproperty_check(&prop, ptr))) {
    BLI_assert(idprop->len == RNA_property_array_length(ptr, prop) ||
               (prop->flag & PROP_IDPROPERTY));
    if (prop->arraydimension == 0) {
      IDP_Int(idprop) = values[0];
    }
    else {
      memcpy(IDP_Array(idprop), values, sizeof(int) * idprop->len);
    }

    rna_idproperty_touch(idprop);
  }
  else if (prop->arraydimension == 0) {
    RNA_property_int_set(ptr, prop, values[0]);
  }
  else if (iprop->setarray) {
    iprop->setarray(ptr, values);
  }
  else if (iprop->setarray_ex) {
    iprop->setarray_ex(ptr, prop, values);
  }
  else if (prop->flag & PROP_EDITABLE) {
    IDPropertyTemplate val = {0};
    IDProperty *group;

    /* TODO: RNA_property_int_clamp_array(ptr, prop, &value); */

    val.array.len = prop->totarraylength;
    val.array.type = IDP_INT;

    group = RNA_struct_idprops(ptr, 1);
    if (group) {
      idprop = IDP_New(IDP_ARRAY, &val, prop->identifier);
      IDP_AddToGroup(group, idprop);
      memcpy(IDP_Array(idprop), values, sizeof(int) * idprop->len);
    }
  }
}

void RNA_property_int_set_index(PointerRNA *ptr, PropertyRNA *prop, int index, int value)
{
  int tmp[RNA_MAX_ARRAY_LENGTH];
  int len = rna_ensure_property_array_length(ptr, prop);

  BLI_assert(RNA_property_type(prop) == PROP_INT);
  BLI_assert(RNA_property_array_check(prop) != false);
  BLI_assert(index >= 0);
  BLI_assert(index < len);

  if (len <= RNA_MAX_ARRAY_LENGTH) {
    RNA_property_int_get_array(ptr, prop, tmp);
    tmp[index] = value;
    RNA_property_int_set_array(ptr, prop, tmp);
  }
  else {
    int *tmparray;

    tmparray = MEM_mallocN(sizeof(int) * len, __func__);
    RNA_property_int_get_array(ptr, prop, tmparray);
    tmparray[index] = value;
    RNA_property_int_set_array(ptr, prop, tmparray);
    MEM_freeN(tmparray);
  }
}

int RNA_property_int_get_default(PointerRNA *UNUSED(ptr), PropertyRNA *prop)
{
  IntPropertyRNA *iprop = (IntPropertyRNA *)rna_ensure_property(prop);

  if (prop->magic != RNA_MAGIC) {
    /* attempt to get the local ID values */
    IDProperty *idp_ui = rna_idproperty_ui(prop);

    if (idp_ui) {
      IDProperty *item;

      item = IDP_GetPropertyTypeFromGroup(idp_ui, "default", IDP_INT);
      return item ? IDP_Int(item) : iprop->defaultvalue;
    }
  }

  return iprop->defaultvalue;
}

bool RNA_property_int_set_default(PointerRNA *ptr, PropertyRNA *prop, int value)
{
  if (value != 0) {
    IDPropertyTemplate val = {
        .i = value,
    };
    return rna_idproperty_ui_set_default(ptr, prop, IDP_INT, &val);
  }
  else {
    return rna_idproperty_ui_set_default(ptr, prop, IDP_INT, NULL);
  }
}

void RNA_property_int_get_default_array(PointerRNA *ptr, PropertyRNA *prop, int *values)
{
  IntPropertyRNA *iprop = (IntPropertyRNA *)rna_ensure_property(prop);

  BLI_assert(RNA_property_type(prop) == PROP_INT);
  BLI_assert(RNA_property_array_check(prop) != false);

  if (prop->magic != RNA_MAGIC) {
    int length = rna_ensure_property_array_length(ptr, prop);

    IDProperty *idp_ui = rna_idproperty_ui(prop);
    IDProperty *item = idp_ui ? IDP_GetPropertyFromGroup(idp_ui, "default") : NULL;

    int defval = (item && item->type == IDP_INT) ? IDP_Int(item) : iprop->defaultvalue;

    if (item && item->type == IDP_ARRAY && item->subtype == IDP_INT) {
      rna_property_int_fill_default_array_values(
          IDP_Array(item), item->len, defval, length, values);
    }
    else {
      rna_property_int_fill_default_array_values(NULL, 0, defval, length, values);
    }
  }
  else if (prop->arraydimension == 0) {
    values[0] = iprop->defaultvalue;
  }
  else {
    rna_property_int_get_default_array_values(ptr, iprop, values);
  }
}

int RNA_property_int_get_default_index(PointerRNA *ptr, PropertyRNA *prop, int index)
{
  int tmp[RNA_MAX_ARRAY_LENGTH];
  int len = rna_ensure_property_array_length(ptr, prop);

  BLI_assert(RNA_property_type(prop) == PROP_INT);
  BLI_assert(RNA_property_array_check(prop) != false);
  BLI_assert(index >= 0);
  BLI_assert(index < len);

  if (len <= RNA_MAX_ARRAY_LENGTH) {
    RNA_property_int_get_default_array(ptr, prop, tmp);
    return tmp[index];
  }
  else {
    int *tmparray, value;

    tmparray = MEM_mallocN(sizeof(int) * len, __func__);
    RNA_property_int_get_default_array(ptr, prop, tmparray);
    value = tmparray[index];
    MEM_freeN(tmparray);

    return value;
  }
}

float RNA_property_float_get(PointerRNA *ptr, PropertyRNA *prop)
{
  FloatPropertyRNA *fprop = (FloatPropertyRNA *)prop;
  IDProperty *idprop;

  BLI_assert(RNA_property_type(prop) == PROP_FLOAT);
  BLI_assert(RNA_property_array_check(prop) == false);

  if ((idprop = rna_idproperty_check(&prop, ptr))) {
    if (idprop->type == IDP_FLOAT) {
      return IDP_Float(idprop);
    }
    else {
      return (float)IDP_Double(idprop);
    }
  }
  else if (fprop->get) {
    return fprop->get(ptr);
  }
  else if (fprop->get_ex) {
    return fprop->get_ex(ptr, prop);
  }
  else {
    return fprop->defaultvalue;
  }
}

void RNA_property_float_set(PointerRNA *ptr, PropertyRNA *prop, float value)
{
  FloatPropertyRNA *fprop = (FloatPropertyRNA *)prop;
  IDProperty *idprop;

  BLI_assert(RNA_property_type(prop) == PROP_FLOAT);
  BLI_assert(RNA_property_array_check(prop) == false);
  /* useful to check on bad values but set function should clamp */
  /* BLI_assert(RNA_property_float_clamp(ptr, prop, &value) == 0); */

  if ((idprop = rna_idproperty_check(&prop, ptr))) {
    RNA_property_float_clamp(ptr, prop, &value);
    if (idprop->type == IDP_FLOAT) {
      IDP_Float(idprop) = value;
    }
    else {
      IDP_Double(idprop) = value;
    }

    rna_idproperty_touch(idprop);
  }
  else if (fprop->set) {
    fprop->set(ptr, value);
  }
  else if (fprop->set_ex) {
    fprop->set_ex(ptr, prop, value);
  }
  else if (prop->flag & PROP_EDITABLE) {
    IDPropertyTemplate val = {0};
    IDProperty *group;

    RNA_property_float_clamp(ptr, prop, &value);

    val.f = value;

    group = RNA_struct_idprops(ptr, 1);
    if (group) {
      IDP_AddToGroup(group, IDP_New(IDP_FLOAT, &val, prop->identifier));
    }
  }
}

static void rna_property_float_fill_default_array_values(
    const float *defarr, int defarr_length, float defvalue, int out_length, float *r_values)
{
  if (defarr && defarr_length > 0) {
    defarr_length = MIN2(defarr_length, out_length);
    memcpy(r_values, defarr, sizeof(float) * defarr_length);
  }
  else {
    defarr_length = 0;
  }

  for (int i = defarr_length; i < out_length; i++) {
    r_values[i] = defvalue;
  }
}

static void rna_property_float_get_default_array_values(PointerRNA *ptr,
                                                        FloatPropertyRNA *fprop,
                                                        float *r_values)
{
  int length = fprop->property.totarraylength;
  int out_length = RNA_property_array_length(ptr, (PropertyRNA *)fprop);

  rna_property_float_fill_default_array_values(
      fprop->defaultarray, length, fprop->defaultvalue, out_length, r_values);
}

void RNA_property_float_get_array(PointerRNA *ptr, PropertyRNA *prop, float *values)
{
  FloatPropertyRNA *fprop = (FloatPropertyRNA *)prop;
  IDProperty *idprop;
  int i;

  BLI_assert(RNA_property_type(prop) == PROP_FLOAT);
  BLI_assert(RNA_property_array_check(prop) != false);

  if ((idprop = rna_idproperty_check(&prop, ptr))) {
    BLI_assert(idprop->len == RNA_property_array_length(ptr, prop) ||
               (prop->flag & PROP_IDPROPERTY));
    if (prop->arraydimension == 0) {
      values[0] = RNA_property_float_get(ptr, prop);
    }
    else if (idprop->subtype == IDP_FLOAT) {
      memcpy(values, IDP_Array(idprop), sizeof(float) * idprop->len);
    }
    else {
      for (i = 0; i < idprop->len; i++) {
        values[i] = (float)(((double *)IDP_Array(idprop))[i]);
      }
    }
  }
  else if (prop->arraydimension == 0) {
    values[0] = RNA_property_float_get(ptr, prop);
  }
  else if (fprop->getarray) {
    fprop->getarray(ptr, values);
  }
  else if (fprop->getarray_ex) {
    fprop->getarray_ex(ptr, prop, values);
  }
  else {
    rna_property_float_get_default_array_values(ptr, fprop, values);
  }
}

void RNA_property_float_get_array_range(PointerRNA *ptr, PropertyRNA *prop, float values[2])
{
  const int array_len = RNA_property_array_length(ptr, prop);

  if (array_len <= 0) {
    values[0] = 0.0f;
    values[1] = 0.0f;
  }
  else if (array_len == 1) {
    RNA_property_float_get_array(ptr, prop, values);
    values[1] = values[0];
  }
  else {
    float arr_stack[32];
    float *arr;
    int i;

    if (array_len > 32) {
      arr = MEM_mallocN(sizeof(float) * array_len, __func__);
    }
    else {
      arr = arr_stack;
    }

    RNA_property_float_get_array(ptr, prop, arr);
    values[0] = values[1] = arr[0];
    for (i = 1; i < array_len; i++) {
      values[0] = MIN2(values[0], arr[i]);
      values[1] = MAX2(values[1], arr[i]);
    }

    if (arr != arr_stack) {
      MEM_freeN(arr);
    }
  }
}

float RNA_property_float_get_index(PointerRNA *ptr, PropertyRNA *prop, int index)
{
  float tmp[RNA_MAX_ARRAY_LENGTH];
  int len = rna_ensure_property_array_length(ptr, prop);

  BLI_assert(RNA_property_type(prop) == PROP_FLOAT);
  BLI_assert(RNA_property_array_check(prop) != false);
  BLI_assert(index >= 0);
  BLI_assert(index < len);

  if (len <= RNA_MAX_ARRAY_LENGTH) {
    RNA_property_float_get_array(ptr, prop, tmp);
    return tmp[index];
  }
  else {
    float *tmparray, value;

    tmparray = MEM_mallocN(sizeof(float) * len, __func__);
    RNA_property_float_get_array(ptr, prop, tmparray);
    value = tmparray[index];
    MEM_freeN(tmparray);

    return value;
  }
}

void RNA_property_float_set_array(PointerRNA *ptr, PropertyRNA *prop, const float *values)
{
  FloatPropertyRNA *fprop = (FloatPropertyRNA *)prop;
  IDProperty *idprop;
  int i;

  BLI_assert(RNA_property_type(prop) == PROP_FLOAT);
  BLI_assert(RNA_property_array_check(prop) != false);

  if ((idprop = rna_idproperty_check(&prop, ptr))) {
    BLI_assert(idprop->len == RNA_property_array_length(ptr, prop) ||
               (prop->flag & PROP_IDPROPERTY));
    if (prop->arraydimension == 0) {
      if (idprop->type == IDP_FLOAT) {
        IDP_Float(idprop) = values[0];
      }
      else {
        IDP_Double(idprop) = values[0];
      }
    }
    else if (idprop->subtype == IDP_FLOAT) {
      memcpy(IDP_Array(idprop), values, sizeof(float) * idprop->len);
    }
    else {
      for (i = 0; i < idprop->len; i++) {
        ((double *)IDP_Array(idprop))[i] = values[i];
      }
    }

    rna_idproperty_touch(idprop);
  }
  else if (prop->arraydimension == 0) {
    RNA_property_float_set(ptr, prop, values[0]);
  }
  else if (fprop->setarray) {
    fprop->setarray(ptr, values);
  }
  else if (fprop->setarray_ex) {
    fprop->setarray_ex(ptr, prop, values);
  }
  else if (prop->flag & PROP_EDITABLE) {
    IDPropertyTemplate val = {0};
    IDProperty *group;

    /* TODO: RNA_property_float_clamp_array(ptr, prop, &value); */

    val.array.len = prop->totarraylength;
    val.array.type = IDP_FLOAT;

    group = RNA_struct_idprops(ptr, 1);
    if (group) {
      idprop = IDP_New(IDP_ARRAY, &val, prop->identifier);
      IDP_AddToGroup(group, idprop);
      memcpy(IDP_Array(idprop), values, sizeof(float) * idprop->len);
    }
  }
}

void RNA_property_float_set_index(PointerRNA *ptr, PropertyRNA *prop, int index, float value)
{
  float tmp[RNA_MAX_ARRAY_LENGTH];
  int len = rna_ensure_property_array_length(ptr, prop);

  BLI_assert(RNA_property_type(prop) == PROP_FLOAT);
  BLI_assert(RNA_property_array_check(prop) != false);
  BLI_assert(index >= 0);
  BLI_assert(index < len);

  if (len <= RNA_MAX_ARRAY_LENGTH) {
    RNA_property_float_get_array(ptr, prop, tmp);
    tmp[index] = value;
    RNA_property_float_set_array(ptr, prop, tmp);
  }
  else {
    float *tmparray;

    tmparray = MEM_mallocN(sizeof(float) * len, __func__);
    RNA_property_float_get_array(ptr, prop, tmparray);
    tmparray[index] = value;
    RNA_property_float_set_array(ptr, prop, tmparray);
    MEM_freeN(tmparray);
  }
}

float RNA_property_float_get_default(PointerRNA *UNUSED(ptr), PropertyRNA *prop)
{
  FloatPropertyRNA *fprop = (FloatPropertyRNA *)rna_ensure_property(prop);

  BLI_assert(RNA_property_type(prop) == PROP_FLOAT);
  BLI_assert(RNA_property_array_check(prop) == false);

  if (prop->magic != RNA_MAGIC) {
    /* attempt to get the local ID values */
    IDProperty *idp_ui = rna_idproperty_ui(prop);

    if (idp_ui) {
      IDProperty *item;

      item = IDP_GetPropertyTypeFromGroup(idp_ui, "default", IDP_DOUBLE);
      return item ? IDP_Double(item) : fprop->defaultvalue;
    }
  }

  return fprop->defaultvalue;
}

bool RNA_property_float_set_default(PointerRNA *ptr, PropertyRNA *prop, float value)
{
  if (value != 0) {
    IDPropertyTemplate val = {
        .d = value,
    };
    return rna_idproperty_ui_set_default(ptr, prop, IDP_DOUBLE, &val);
  }
  else {
    return rna_idproperty_ui_set_default(ptr, prop, IDP_DOUBLE, NULL);
  }
}

void RNA_property_float_get_default_array(PointerRNA *ptr, PropertyRNA *prop, float *values)
{
  FloatPropertyRNA *fprop = (FloatPropertyRNA *)rna_ensure_property(prop);

  BLI_assert(RNA_property_type(prop) == PROP_FLOAT);
  BLI_assert(RNA_property_array_check(prop) != false);

  if (prop->magic != RNA_MAGIC) {
    int length = rna_ensure_property_array_length(ptr, prop);

    IDProperty *idp_ui = rna_idproperty_ui(prop);
    IDProperty *item = idp_ui ? IDP_GetPropertyFromGroup(idp_ui, "default") : NULL;

    float defval = (item && item->type == IDP_DOUBLE) ? IDP_Double(item) : fprop->defaultvalue;

    if (item && item->type == IDP_ARRAY && item->subtype == IDP_DOUBLE) {
      double *defarr = IDP_Array(item);
      for (int i = 0; i < length; i++) {
        values[i] = (i < item->len) ? (float)defarr[i] : defval;
      }
    }
    else if (item && item->type == IDP_ARRAY && item->subtype == IDP_FLOAT) {
      rna_property_float_fill_default_array_values(
          IDP_Array(item), item->len, defval, length, values);
    }
    else {
      rna_property_float_fill_default_array_values(NULL, 0, defval, length, values);
    }
  }
  else if (prop->arraydimension == 0) {
    values[0] = fprop->defaultvalue;
  }
  else {
    rna_property_float_get_default_array_values(ptr, fprop, values);
  }
}

float RNA_property_float_get_default_index(PointerRNA *ptr, PropertyRNA *prop, int index)
{
  float tmp[RNA_MAX_ARRAY_LENGTH];
  int len = rna_ensure_property_array_length(ptr, prop);

  BLI_assert(RNA_property_type(prop) == PROP_FLOAT);
  BLI_assert(RNA_property_array_check(prop) != false);
  BLI_assert(index >= 0);
  BLI_assert(index < len);

  if (len <= RNA_MAX_ARRAY_LENGTH) {
    RNA_property_float_get_default_array(ptr, prop, tmp);
    return tmp[index];
  }
  else {
    float *tmparray, value;

    tmparray = MEM_mallocN(sizeof(float) * len, __func__);
    RNA_property_float_get_default_array(ptr, prop, tmparray);
    value = tmparray[index];
    MEM_freeN(tmparray);

    return value;
  }
}

void RNA_property_string_get(PointerRNA *ptr, PropertyRNA *prop, char *value)
{
  StringPropertyRNA *sprop = (StringPropertyRNA *)prop;
  IDProperty *idprop;

  BLI_assert(RNA_property_type(prop) == PROP_STRING);

  if ((idprop = rna_idproperty_check(&prop, ptr))) {
    /* editing bytes is not 100% supported
     * since they can contain NIL chars */
    if (idprop->subtype == IDP_STRING_SUB_BYTE) {
      memcpy(value, IDP_String(idprop), idprop->len);
      value[idprop->len] = '\0';
    }
    else {
      memcpy(value, IDP_String(idprop), idprop->len);
    }
  }
  else if (sprop->get) {
    sprop->get(ptr, value);
  }
  else if (sprop->get_ex) {
    sprop->get_ex(ptr, prop, value);
  }
  else {
    strcpy(value, sprop->defaultvalue);
  }
}

char *RNA_property_string_get_alloc(
    PointerRNA *ptr, PropertyRNA *prop, char *fixedbuf, int fixedlen, int *r_len)
{
  char *buf;
  int length;

  BLI_assert(RNA_property_type(prop) == PROP_STRING);

  length = RNA_property_string_length(ptr, prop);

  if (length + 1 < fixedlen) {
    buf = fixedbuf;
  }
  else {
    buf = MEM_mallocN(sizeof(char) * (length + 1), "RNA_string_get_alloc");
  }

#ifndef NDEBUG
  /* safety check to ensure the string is actually set */
  buf[length] = 255;
#endif

  RNA_property_string_get(ptr, prop, buf);

#ifndef NDEBUG
  BLI_assert(buf[length] == '\0');
#endif

  if (r_len) {
    *r_len = length;
  }

  return buf;
}

/* this is the length without \0 terminator */
int RNA_property_string_length(PointerRNA *ptr, PropertyRNA *prop)
{
  StringPropertyRNA *sprop = (StringPropertyRNA *)prop;
  IDProperty *idprop;

  BLI_assert(RNA_property_type(prop) == PROP_STRING);

  if ((idprop = rna_idproperty_check(&prop, ptr))) {
    if (idprop->subtype == IDP_STRING_SUB_BYTE) {
      return idprop->len;
    }
    else {
#ifndef NDEBUG
      /* these _must_ stay in sync */
      BLI_assert(strlen(IDP_String(idprop)) == idprop->len - 1);
#endif
      return idprop->len - 1;
    }
  }
  else if (sprop->length) {
    return sprop->length(ptr);
  }
  else if (sprop->length_ex) {
    return sprop->length_ex(ptr, prop);
  }
  else {
    return strlen(sprop->defaultvalue);
  }
}

void RNA_property_string_set(PointerRNA *ptr, PropertyRNA *prop, const char *value)
{
  StringPropertyRNA *sprop = (StringPropertyRNA *)prop;
  IDProperty *idprop;

  BLI_assert(RNA_property_type(prop) == PROP_STRING);

  if ((idprop = rna_idproperty_check(&prop, ptr))) {
    /* both IDP_STRING_SUB_BYTE / IDP_STRING_SUB_UTF8 */
    IDP_AssignString(idprop, value, RNA_property_string_maxlength(prop) - 1);
    rna_idproperty_touch(idprop);
  }
  else if (sprop->set) {
    sprop->set(ptr, value); /* set function needs to clamp its self */
  }
  else if (sprop->set_ex) {
    sprop->set_ex(ptr, prop, value); /* set function needs to clamp its self */
  }
  else if (prop->flag & PROP_EDITABLE) {
    IDProperty *group;

    group = RNA_struct_idprops(ptr, 1);
    if (group) {
      IDP_AddToGroup(group,
                     IDP_NewString(value, prop->identifier, RNA_property_string_maxlength(prop)));
    }
  }
}

void RNA_property_string_set_bytes(PointerRNA *ptr, PropertyRNA *prop, const char *value, int len)
{
  StringPropertyRNA *sprop = (StringPropertyRNA *)prop;
  IDProperty *idprop;

  BLI_assert(RNA_property_type(prop) == PROP_STRING);
  BLI_assert(RNA_property_subtype(prop) == PROP_BYTESTRING);

  if ((idprop = rna_idproperty_check(&prop, ptr))) {
    IDP_ResizeArray(idprop, len);
    memcpy(idprop->data.pointer, value, (size_t)len);

    rna_idproperty_touch(idprop);
  }
  else if (sprop->set) {
    /* XXX, should take length argument (currently not used). */
    sprop->set(ptr, value); /* set function needs to clamp its self */
  }
  else if (sprop->set_ex) {
    /* XXX, should take length argument (currently not used). */
    sprop->set_ex(ptr, prop, value); /* set function needs to clamp its self */
  }
  else if (prop->flag & PROP_EDITABLE) {
    IDProperty *group;

    group = RNA_struct_idprops(ptr, 1);
    if (group) {
      IDPropertyTemplate val = {0};
      val.string.str = value;
      val.string.len = len;
      val.string.subtype = IDP_STRING_SUB_BYTE;
      IDP_AddToGroup(group, IDP_New(IDP_STRING, &val, prop->identifier));
    }
  }
}

void RNA_property_string_get_default(PointerRNA *UNUSED(ptr), PropertyRNA *prop, char *value)
{
  StringPropertyRNA *sprop = (StringPropertyRNA *)rna_ensure_property(prop);

  BLI_assert(RNA_property_type(prop) == PROP_STRING);

  strcpy(value, sprop->defaultvalue);
}

char *RNA_property_string_get_default_alloc(PointerRNA *ptr,
                                            PropertyRNA *prop,
                                            char *fixedbuf,
                                            int fixedlen)
{
  char *buf;
  int length;

  BLI_assert(RNA_property_type(prop) == PROP_STRING);

  length = RNA_property_string_default_length(ptr, prop);

  if (length + 1 < fixedlen) {
    buf = fixedbuf;
  }
  else {
    buf = MEM_callocN(sizeof(char) * (length + 1), "RNA_string_get_alloc");
  }

  RNA_property_string_get_default(ptr, prop, buf);

  return buf;
}

/* this is the length without \0 terminator */
int RNA_property_string_default_length(PointerRNA *UNUSED(ptr), PropertyRNA *prop)
{
  StringPropertyRNA *sprop = (StringPropertyRNA *)rna_ensure_property(prop);

  BLI_assert(RNA_property_type(prop) == PROP_STRING);

  return strlen(sprop->defaultvalue);
}

int RNA_property_enum_get(PointerRNA *ptr, PropertyRNA *prop)
{
  EnumPropertyRNA *eprop = (EnumPropertyRNA *)prop;
  IDProperty *idprop;

  BLI_assert(RNA_property_type(prop) == PROP_ENUM);

  if ((idprop = rna_idproperty_check(&prop, ptr))) {
    return IDP_Int(idprop);
  }
  else if (eprop->get) {
    return eprop->get(ptr);
  }
  else if (eprop->get_ex) {
    return eprop->get_ex(ptr, prop);
  }
  else {
    return eprop->defaultvalue;
  }
}

void RNA_property_enum_set(PointerRNA *ptr, PropertyRNA *prop, int value)
{
  EnumPropertyRNA *eprop = (EnumPropertyRNA *)prop;
  IDProperty *idprop;

  BLI_assert(RNA_property_type(prop) == PROP_ENUM);

  if ((idprop = rna_idproperty_check(&prop, ptr))) {
    IDP_Int(idprop) = value;
    rna_idproperty_touch(idprop);
  }
  else if (eprop->set) {
    eprop->set(ptr, value);
  }
  else if (eprop->set_ex) {
    eprop->set_ex(ptr, prop, value);
  }
  else if (prop->flag & PROP_EDITABLE) {
    IDPropertyTemplate val = {0};
    IDProperty *group;

    val.i = value;

    group = RNA_struct_idprops(ptr, 1);
    if (group) {
      IDP_AddToGroup(group, IDP_New(IDP_INT, &val, prop->identifier));
    }
  }
}

int RNA_property_enum_get_default(PointerRNA *UNUSED(ptr), PropertyRNA *prop)
{
  EnumPropertyRNA *eprop = (EnumPropertyRNA *)rna_ensure_property(prop);

  BLI_assert(RNA_property_type(prop) == PROP_ENUM);

  return eprop->defaultvalue;
}

void *RNA_property_enum_py_data_get(PropertyRNA *prop)
{
  EnumPropertyRNA *eprop = (EnumPropertyRNA *)prop;

  BLI_assert(RNA_property_type(prop) == PROP_ENUM);

  return eprop->py_data;
}

/**
 * Get the value of the item that is \a step items away from \a from_value.
 *
 * \param from_value: Item value to start stepping from.
 * \param step: Absolute value defines step size, sign defines direction.
 *              E.g to get the next item, pass 1, for the previous -1.
 */
int RNA_property_enum_step(
    const bContext *C, PointerRNA *ptr, PropertyRNA *prop, int from_value, int step)
{
  const EnumPropertyItem *item_array;
  int totitem;
  bool free;
  int result_value = from_value;
  int i, i_init;
  int single_step = (step < 0) ? -1 : 1;
  int step_tot = 0;

  RNA_property_enum_items((bContext *)C, ptr, prop, &item_array, &totitem, &free);
  i = RNA_enum_from_value(item_array, from_value);
  i_init = i;

  do {
    i = mod_i(i + single_step, totitem);
    if (item_array[i].identifier[0]) {
      step_tot += single_step;
    }
  } while ((i != i_init) && (step_tot != step));

  if (i != i_init) {
    result_value = item_array[i].value;
  }

  if (free) {
    MEM_freeN((void *)item_array);
  }

  return result_value;
}

PointerRNA RNA_property_pointer_get(PointerRNA *ptr, PropertyRNA *prop)
{
  PointerPropertyRNA *pprop = (PointerPropertyRNA *)prop;
  IDProperty *idprop;

  BLI_assert(RNA_property_type(prop) == PROP_POINTER);

  if ((idprop = rna_idproperty_check(&prop, ptr))) {
    pprop = (PointerPropertyRNA *)prop;

    if (RNA_struct_is_ID(pprop->type)) {
      return rna_pointer_inherit_refine(ptr, pprop->type, IDP_Id(idprop));
    }

    /* for groups, data is idprop itself */
    if (pprop->typef) {
      return rna_pointer_inherit_refine(ptr, pprop->typef(ptr), idprop);
    }
    else {
      return rna_pointer_inherit_refine(ptr, pprop->type, idprop);
    }
  }
  else if (pprop->get) {
    return pprop->get(ptr);
  }
  else if (prop->flag & PROP_IDPROPERTY) {
    /* XXX temporary hack to add it automatically, reading should
     * never do any write ops, to ensure thread safety etc .. */
    RNA_property_pointer_add(ptr, prop);
    return RNA_property_pointer_get(ptr, prop);
  }
  else {
    return PointerRNA_NULL;
  }
}

void RNA_property_pointer_set(PointerRNA *ptr,
                              PropertyRNA *prop,
                              PointerRNA ptr_value,
                              ReportList *reports)
{
  PointerPropertyRNA *pprop = (PointerPropertyRNA *)prop;
  IDProperty *idprop = rna_idproperty_check(&prop, ptr);
  BLI_assert(RNA_property_type(prop) == PROP_POINTER);

  /* Check types. */
  if (pprop->set != NULL) {
    /* Assigning to a real RNA property. */
    if (ptr_value.type != NULL && !RNA_struct_is_a(ptr_value.type, pprop->type)) {
      BKE_reportf(reports,
                  RPT_ERROR,
                  "%s: expected %s type, not %s",
                  __func__,
                  pprop->type->identifier,
                  ptr_value.type->identifier);
      return;
    }
  }
  else {
    /* Assigning to an IDProperty desguised as RNA one. */
    if (ptr_value.type != NULL && !RNA_struct_is_a(ptr_value.type, &RNA_ID)) {
      BKE_reportf(reports,
                  RPT_ERROR,
                  "%s: expected ID type, not %s",
                  __func__,
                  ptr_value.type->identifier);
      return;
    }
  }

  /* We got an existing IDProperty. */
  if (idprop != NULL) {
    /* Not-yet-defined ID IDProps have an IDP_GROUP type, not an IDP_ID one - because of reasons?
     * XXX This has to be investigated fully - there might be a good reason for it, but off hands
     * this seems really weird... */
    if (idprop->type == IDP_ID) {
      IDP_AssignID(idprop, ptr_value.data, 0);
      rna_idproperty_touch(idprop);
    }
    else {
      BLI_assert(idprop->type == IDP_GROUP);

      IDPropertyTemplate val = {.id = ptr_value.data};
      IDProperty *group = RNA_struct_idprops(ptr, true);
      BLI_assert(group != NULL);

      IDP_ReplaceInGroup_ex(group, IDP_New(IDP_ID, &val, idprop->name), idprop);
    }
  }
  /* RNA property. */
  else if (pprop->set && !((prop->flag & PROP_NEVER_NULL) && ptr_value.data == NULL) &&
           !((prop->flag & PROP_ID_SELF_CHECK) && ptr->owner_id == ptr_value.owner_id)) {
    pprop->set(ptr, ptr_value, reports);
  }
  /* IDProperty desguised as RNA property (and not yet defined in ptr). */
  else if (prop->flag & PROP_EDITABLE) {
    IDPropertyTemplate val = {0};
    IDProperty *group;

    val.id = ptr_value.data;

    group = RNA_struct_idprops(ptr, true);
    if (group) {
      IDP_ReplaceInGroup(group, IDP_New(IDP_ID, &val, prop->identifier));
    }
  }
}

PointerRNA RNA_property_pointer_get_default(PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop))
{
  /*PointerPropertyRNA *pprop = (PointerPropertyRNA *)prop; */

  /* BLI_assert(RNA_property_type(prop) == PROP_POINTER); */

  return PointerRNA_NULL; /* FIXME: there has to be a way... */
}

void RNA_property_pointer_add(PointerRNA *ptr, PropertyRNA *prop)
{
  /*IDProperty *idprop;*/

  BLI_assert(RNA_property_type(prop) == PROP_POINTER);

  if ((/*idprop=*/rna_idproperty_check(&prop, ptr))) {
    /* already exists */
  }
  else if (prop->flag & PROP_IDPROPERTY) {
    IDPropertyTemplate val = {0};
    IDProperty *group;

    val.i = 0;

    group = RNA_struct_idprops(ptr, 1);
    if (group) {
      IDP_AddToGroup(group, IDP_New(IDP_GROUP, &val, prop->identifier));
    }
  }
  else {
    printf("%s %s.%s: only supported for id properties.\n",
           __func__,
           ptr->type->identifier,
           prop->identifier);
  }
}

void RNA_property_pointer_remove(PointerRNA *ptr, PropertyRNA *prop)
{
  IDProperty *idprop, *group;

  BLI_assert(RNA_property_type(prop) == PROP_POINTER);

  if ((idprop = rna_idproperty_check(&prop, ptr))) {
    group = RNA_struct_idprops(ptr, 0);

    if (group) {
      IDP_FreeFromGroup(group, idprop);
    }
  }
  else {
    printf("%s %s.%s: only supported for id properties.\n",
           __func__,
           ptr->type->identifier,
           prop->identifier);
  }
}

static void rna_property_collection_get_idp(CollectionPropertyIterator *iter)
{
  CollectionPropertyRNA *cprop = (CollectionPropertyRNA *)iter->prop;

  iter->ptr.data = rna_iterator_array_get(iter);
  iter->ptr.type = cprop->item_type;
  rna_pointer_inherit_id(cprop->item_type, &iter->parent, &iter->ptr);
}

void RNA_property_collection_begin(PointerRNA *ptr,
                                   PropertyRNA *prop,
                                   CollectionPropertyIterator *iter)
{
  IDProperty *idprop;

  BLI_assert(RNA_property_type(prop) == PROP_COLLECTION);

  memset(iter, 0, sizeof(*iter));

  if ((idprop = rna_idproperty_check(&prop, ptr)) || (prop->flag & PROP_IDPROPERTY)) {
    iter->parent = *ptr;
    iter->prop = prop;

    if (idprop) {
      rna_iterator_array_begin(
          iter, IDP_IDPArray(idprop), sizeof(IDProperty), idprop->len, 0, NULL);
    }
    else {
      rna_iterator_array_begin(iter, NULL, sizeof(IDProperty), 0, 0, NULL);
    }

    if (iter->valid) {
      rna_property_collection_get_idp(iter);
    }

    iter->idprop = 1;
  }
  else {
    CollectionPropertyRNA *cprop = (CollectionPropertyRNA *)prop;
    cprop->begin(iter, ptr);
  }
}

void RNA_property_collection_next(CollectionPropertyIterator *iter)
{
  CollectionPropertyRNA *cprop = (CollectionPropertyRNA *)rna_ensure_property(iter->prop);

  if (iter->idprop) {
    rna_iterator_array_next(iter);

    if (iter->valid) {
      rna_property_collection_get_idp(iter);
    }
  }
  else {
    cprop->next(iter);
  }
}

void RNA_property_collection_skip(CollectionPropertyIterator *iter, int num)
{
  CollectionPropertyRNA *cprop = (CollectionPropertyRNA *)rna_ensure_property(iter->prop);
  int i;

  if (num > 1 && (iter->idprop || (cprop->property.flag_internal & PROP_INTERN_RAW_ARRAY))) {
    /* fast skip for array */
    ArrayIterator *internal = &iter->internal.array;

    if (!internal->skip) {
      internal->ptr += internal->itemsize * (num - 1);
      iter->valid = (internal->ptr < internal->endptr);
      if (iter->valid) {
        RNA_property_collection_next(iter);
      }
      return;
    }
  }

  /* slow iteration otherwise */
  for (i = 0; i < num && iter->valid; i++) {
    RNA_property_collection_next(iter);
  }
}

void RNA_property_collection_end(CollectionPropertyIterator *iter)
{
  CollectionPropertyRNA *cprop = (CollectionPropertyRNA *)rna_ensure_property(iter->prop);

  if (iter->idprop) {
    rna_iterator_array_end(iter);
  }
  else {
    cprop->end(iter);
  }
}

int RNA_property_collection_length(PointerRNA *ptr, PropertyRNA *prop)
{
  CollectionPropertyRNA *cprop = (CollectionPropertyRNA *)prop;
  IDProperty *idprop;

  BLI_assert(RNA_property_type(prop) == PROP_COLLECTION);

  if ((idprop = rna_idproperty_check(&prop, ptr))) {
    return idprop->len;
  }
  else if (cprop->length) {
    return cprop->length(ptr);
  }
  else {
    CollectionPropertyIterator iter;
    int length = 0;

    RNA_property_collection_begin(ptr, prop, &iter);
    for (; iter.valid; RNA_property_collection_next(&iter)) {
      length++;
    }
    RNA_property_collection_end(&iter);

    return length;
  }
}

void RNA_property_collection_add(PointerRNA *ptr, PropertyRNA *prop, PointerRNA *r_ptr)
{
  IDProperty *idprop;
  /*  CollectionPropertyRNA *cprop = (CollectionPropertyRNA *)prop; */

  BLI_assert(RNA_property_type(prop) == PROP_COLLECTION);

  if ((idprop = rna_idproperty_check(&prop, ptr))) {
    IDPropertyTemplate val = {0};
    IDProperty *item;

    item = IDP_New(IDP_GROUP, &val, "");
    IDP_AppendArray(idprop, item);
    /* IDP_AppendArray does a shallow copy (memcpy), only free memory  */
    /* IDP_FreePropertyContent(item); */
    MEM_freeN(item);
    rna_idproperty_touch(idprop);
  }
  else if (prop->flag & PROP_IDPROPERTY) {
    IDProperty *group, *item;
    IDPropertyTemplate val = {0};

    group = RNA_struct_idprops(ptr, 1);
    if (group) {
      idprop = IDP_NewIDPArray(prop->identifier);
      IDP_AddToGroup(group, idprop);

      item = IDP_New(IDP_GROUP, &val, "");
      IDP_AppendArray(idprop, item);
      /* IDP_AppendArray does a shallow copy (memcpy), only free memory */
      /* IDP_FreePropertyContent(item); */
      MEM_freeN(item);
    }
  }

  /* py api calls directly */
#if 0
  else if (cprop->add) {
    if (!(cprop->add->flag & FUNC_USE_CONTEXT)) { /* XXX check for this somewhere else */
      ParameterList params;
      RNA_parameter_list_create(&params, ptr, cprop->add);
      RNA_function_call(NULL, NULL, ptr, cprop->add, &params);
      RNA_parameter_list_free(&params);
    }
  }
#  if 0
  else {
    printf("%s %s.%s: not implemented for this property.\n",
           __func__,
           ptr->type->identifier,
           prop->identifier);
  }
#  endif
#endif

  if (r_ptr) {
    if (idprop) {
      CollectionPropertyRNA *cprop = (CollectionPropertyRNA *)prop;

      r_ptr->data = IDP_GetIndexArray(idprop, idprop->len - 1);
      r_ptr->type = cprop->item_type;
      rna_pointer_inherit_id(NULL, ptr, r_ptr);
    }
    else {
      memset(r_ptr, 0, sizeof(*r_ptr));
    }
  }
}

bool RNA_property_collection_remove(PointerRNA *ptr, PropertyRNA *prop, int key)
{
  IDProperty *idprop;
  /*  CollectionPropertyRNA *cprop = (CollectionPropertyRNA *)prop; */

  BLI_assert(RNA_property_type(prop) == PROP_COLLECTION);

  if ((idprop = rna_idproperty_check(&prop, ptr))) {
    IDProperty tmp, *array;
    int len;

    len = idprop->len;
    array = IDP_IDPArray(idprop);

    if (key >= 0 && key < len) {
      if (key + 1 < len) {
        /* move element to be removed to the back */
        memcpy(&tmp, &array[key], sizeof(IDProperty));
        memmove(array + key, array + key + 1, sizeof(IDProperty) * (len - (key + 1)));
        memcpy(&array[len - 1], &tmp, sizeof(IDProperty));
      }

      IDP_ResizeIDPArray(idprop, len - 1);
    }

    return true;
  }
  else if (prop->flag & PROP_IDPROPERTY) {
    return true;
  }

  /* py api calls directly */
#if 0
  else if (cprop->remove) {
    if (!(cprop->remove->flag & FUNC_USE_CONTEXT)) { /* XXX check for this somewhere else */
      ParameterList params;
      RNA_parameter_list_create(&params, ptr, cprop->remove);
      RNA_function_call(NULL, NULL, ptr, cprop->remove, &params);
      RNA_parameter_list_free(&params);
    }

    return false;
  }
#  if 0
  else {
    printf("%s %s.%s: only supported for id properties.\n",
           __func__,
           ptr->type->identifier,
           prop->identifier);
  }
#  endif
#endif
  return false;
}

bool RNA_property_collection_move(PointerRNA *ptr, PropertyRNA *prop, int key, int pos)
{
  IDProperty *idprop;

  BLI_assert(RNA_property_type(prop) == PROP_COLLECTION);

  if ((idprop = rna_idproperty_check(&prop, ptr))) {
    IDProperty tmp, *array;
    int len;

    len = idprop->len;
    array = IDP_IDPArray(idprop);

    if (key >= 0 && key < len && pos >= 0 && pos < len && key != pos) {
      memcpy(&tmp, &array[key], sizeof(IDProperty));
      if (pos < key) {
        memmove(array + pos + 1, array + pos, sizeof(IDProperty) * (key - pos));
      }
      else {
        memmove(array + key, array + key + 1, sizeof(IDProperty) * (pos - key));
      }
      memcpy(&array[pos], &tmp, sizeof(IDProperty));
    }

    return true;
  }
  else if (prop->flag & PROP_IDPROPERTY) {
    return true;
  }

  return false;
}

void RNA_property_collection_clear(PointerRNA *ptr, PropertyRNA *prop)
{
  IDProperty *idprop;

  BLI_assert(RNA_property_type(prop) == PROP_COLLECTION);

  if ((idprop = rna_idproperty_check(&prop, ptr))) {
    IDP_ResizeIDPArray(idprop, 0);
    rna_idproperty_touch(idprop);
  }
}

int RNA_property_collection_lookup_index(PointerRNA *ptr, PropertyRNA *prop, PointerRNA *t_ptr)
{
  CollectionPropertyIterator iter;
  int index = 0;

  BLI_assert(RNA_property_type(prop) == PROP_COLLECTION);

  RNA_property_collection_begin(ptr, prop, &iter);
  for (index = 0; iter.valid; RNA_property_collection_next(&iter), index++) {
    if (iter.ptr.data == t_ptr->data) {
      break;
    }
  }
  RNA_property_collection_end(&iter);

  /* did we find it? */
  if (iter.valid) {
    return index;
  }
  else {
    return -1;
  }
}

int RNA_property_collection_lookup_int(PointerRNA *ptr,
                                       PropertyRNA *prop,
                                       int key,
                                       PointerRNA *r_ptr)
{
  CollectionPropertyRNA *cprop = (CollectionPropertyRNA *)rna_ensure_property(prop);

  BLI_assert(RNA_property_type(prop) == PROP_COLLECTION);

  if (cprop->lookupint) {
    /* we have a callback defined, use it */
    return cprop->lookupint(ptr, key, r_ptr);
  }
  else {
    /* no callback defined, just iterate and find the nth item */
    CollectionPropertyIterator iter;
    int i;

    RNA_property_collection_begin(ptr, prop, &iter);
    for (i = 0; iter.valid; RNA_property_collection_next(&iter), i++) {
      if (i == key) {
        *r_ptr = iter.ptr;
        break;
      }
    }
    RNA_property_collection_end(&iter);

    if (!iter.valid) {
      memset(r_ptr, 0, sizeof(*r_ptr));
    }

    return iter.valid;
  }
}

int RNA_property_collection_lookup_string(PointerRNA *ptr,
                                          PropertyRNA *prop,
                                          const char *key,
                                          PointerRNA *r_ptr)
{
  CollectionPropertyRNA *cprop = (CollectionPropertyRNA *)rna_ensure_property(prop);

  BLI_assert(RNA_property_type(prop) == PROP_COLLECTION);

  if (cprop->lookupstring) {
    /* we have a callback defined, use it */
    return cprop->lookupstring(ptr, key, r_ptr);
  }
  else {
    /* no callback defined, compare with name properties if they exist */
    CollectionPropertyIterator iter;
    PropertyRNA *nameprop;
    char name[256], *nameptr;
    int found = 0;
    int keylen = strlen(key);
    int namelen;

    RNA_property_collection_begin(ptr, prop, &iter);
    for (; iter.valid; RNA_property_collection_next(&iter)) {
      if (iter.ptr.data && iter.ptr.type->nameproperty) {
        nameprop = iter.ptr.type->nameproperty;

        nameptr = RNA_property_string_get_alloc(&iter.ptr, nameprop, name, sizeof(name), &namelen);

        if ((keylen == namelen) && STREQ(nameptr, key)) {
          *r_ptr = iter.ptr;
          found = 1;
        }

        if ((char *)&name != nameptr) {
          MEM_freeN(nameptr);
        }

        if (found) {
          break;
        }
      }
    }
    RNA_property_collection_end(&iter);

    if (!iter.valid) {
      memset(r_ptr, 0, sizeof(*r_ptr));
    }

    return iter.valid;
  }
}

/* zero return is an assignment error */
int RNA_property_collection_assign_int(PointerRNA *ptr,
                                       PropertyRNA *prop,
                                       const int key,
                                       const PointerRNA *assign_ptr)
{
  CollectionPropertyRNA *cprop = (CollectionPropertyRNA *)rna_ensure_property(prop);

  BLI_assert(RNA_property_type(prop) == PROP_COLLECTION);

  if (cprop->assignint) {
    /* we have a callback defined, use it */
    return cprop->assignint(ptr, key, assign_ptr);
  }

  return 0;
}

bool RNA_property_collection_type_get(PointerRNA *ptr, PropertyRNA *prop, PointerRNA *r_ptr)
{
  BLI_assert(RNA_property_type(prop) == PROP_COLLECTION);

  *r_ptr = *ptr;
  return ((r_ptr->type = rna_ensure_property(prop)->srna) ? 1 : 0);
}

int RNA_property_collection_raw_array(PointerRNA *ptr,
                                      PropertyRNA *prop,
                                      PropertyRNA *itemprop,
                                      RawArray *array)
{
  CollectionPropertyIterator iter;
  ArrayIterator *internal;
  char *arrayp;

  BLI_assert(RNA_property_type(prop) == PROP_COLLECTION);

  if (!(prop->flag_internal & PROP_INTERN_RAW_ARRAY) ||
      !(itemprop->flag_internal & PROP_INTERN_RAW_ACCESS)) {
    return 0;
  }

  RNA_property_collection_begin(ptr, prop, &iter);

  if (iter.valid) {
    /* get data from array iterator and item property */
    internal = &iter.internal.array;
    arrayp = (iter.valid) ? iter.ptr.data : NULL;

    if (internal->skip || !RNA_property_editable(&iter.ptr, itemprop)) {
      /* we might skip some items, so it's not a proper array */
      RNA_property_collection_end(&iter);
      return 0;
    }

    array->array = arrayp + itemprop->rawoffset;
    array->stride = internal->itemsize;
    array->len = ((char *)internal->endptr - arrayp) / internal->itemsize;
    array->type = itemprop->rawtype;
  }
  else {
    memset(array, 0, sizeof(RawArray));
  }

  RNA_property_collection_end(&iter);

  return 1;
}

#define RAW_GET(dtype, var, raw, a) \
  { \
    switch (raw.type) { \
      case PROP_RAW_CHAR: \
        var = (dtype)((char *)raw.array)[a]; \
        break; \
      case PROP_RAW_SHORT: \
        var = (dtype)((short *)raw.array)[a]; \
        break; \
      case PROP_RAW_INT: \
        var = (dtype)((int *)raw.array)[a]; \
        break; \
      case PROP_RAW_BOOLEAN: \
        var = (dtype)((bool *)raw.array)[a]; \
        break; \
      case PROP_RAW_FLOAT: \
        var = (dtype)((float *)raw.array)[a]; \
        break; \
      case PROP_RAW_DOUBLE: \
        var = (dtype)((double *)raw.array)[a]; \
        break; \
      default: \
        var = (dtype)0; \
    } \
  } \
  (void)0

#define RAW_SET(dtype, raw, a, var) \
  { \
    switch (raw.type) { \
      case PROP_RAW_CHAR: \
        ((char *)raw.array)[a] = (char)var; \
        break; \
      case PROP_RAW_SHORT: \
        ((short *)raw.array)[a] = (short)var; \
        break; \
      case PROP_RAW_INT: \
        ((int *)raw.array)[a] = (int)var; \
        break; \
      case PROP_RAW_BOOLEAN: \
        ((bool *)raw.array)[a] = (bool)var; \
        break; \
      case PROP_RAW_FLOAT: \
        ((float *)raw.array)[a] = (float)var; \
        break; \
      case PROP_RAW_DOUBLE: \
        ((double *)raw.array)[a] = (double)var; \
        break; \
      default: \
        break; \
    } \
  } \
  (void)0

int RNA_raw_type_sizeof(RawPropertyType type)
{
  switch (type) {
    case PROP_RAW_CHAR:
      return sizeof(char);
    case PROP_RAW_SHORT:
      return sizeof(short);
    case PROP_RAW_INT:
      return sizeof(int);
    case PROP_RAW_BOOLEAN:
      return sizeof(bool);
    case PROP_RAW_FLOAT:
      return sizeof(float);
    case PROP_RAW_DOUBLE:
      return sizeof(double);
    default:
      return 0;
  }
}

static int rna_property_array_length_all_dimensions(PointerRNA *ptr, PropertyRNA *prop)
{
  int i, len[RNA_MAX_ARRAY_DIMENSION];
  const int dim = RNA_property_array_dimension(ptr, prop, len);
  int size;

  if (dim == 0) {
    return 0;
  }

  for (size = 1, i = 0; i < dim; i++) {
    size *= len[i];
  }

  return size;
}

static int rna_raw_access(ReportList *reports,
                          PointerRNA *ptr,
                          PropertyRNA *prop,
                          const char *propname,
                          void *inarray,
                          RawPropertyType intype,
                          int inlen,
                          int set)
{
  StructRNA *ptype;
  PointerRNA itemptr_base;
  PropertyRNA *itemprop, *iprop;
  PropertyType itemtype = 0;
  RawArray in;
  int itemlen = 0;

  /* initialize in array, stride assumed 0 in following code */
  in.array = inarray;
  in.type = intype;
  in.len = inlen;
  in.stride = 0;

  ptype = RNA_property_pointer_type(ptr, prop);

  /* try to get item property pointer */
  RNA_pointer_create(NULL, ptype, NULL, &itemptr_base);
  itemprop = RNA_struct_find_property(&itemptr_base, propname);

  if (itemprop) {
    /* we have item property pointer */
    RawArray out;

    /* check type */
    itemtype = RNA_property_type(itemprop);

    if (!ELEM(itemtype, PROP_BOOLEAN, PROP_INT, PROP_FLOAT)) {
      BKE_report(reports, RPT_ERROR, "Only boolean, int and float properties supported");
      return 0;
    }

    /* check item array */
    itemlen = RNA_property_array_length(&itemptr_base, itemprop);

    /* dynamic array? need to get length per item */
    if (itemprop->getlength) {
      itemprop = NULL;
    }
    /* try to access as raw array */
    else if (RNA_property_collection_raw_array(ptr, prop, itemprop, &out)) {
      int arraylen = (itemlen == 0) ? 1 : itemlen;
      if (in.len != arraylen * out.len) {
        BKE_reportf(reports,
                    RPT_ERROR,
                    "Array length mismatch (expected %d, got %d)",
                    out.len * arraylen,
                    in.len);
        return 0;
      }

      /* matching raw types */
      if (out.type == in.type) {
        void *inp = in.array;
        void *outp = out.array;
        int a, size;

        size = RNA_raw_type_sizeof(out.type) * arraylen;

        for (a = 0; a < out.len; a++) {
          if (set) {
            memcpy(outp, inp, size);
          }
          else {
            memcpy(inp, outp, size);
          }

          inp = (char *)inp + size;
          outp = (char *)outp + out.stride;
        }

        return 1;
      }

      /* could also be faster with non-matching types,
       * for now we just do slower loop .. */
    }
  }

  {
    void *tmparray = NULL;
    int tmplen = 0;
    int err = 0, j, a = 0;
    int needconv = 1;

    if (((itemtype == PROP_INT) && (in.type == PROP_RAW_INT)) ||
        ((itemtype == PROP_BOOLEAN) && (in.type == PROP_RAW_BOOLEAN)) ||
        ((itemtype == PROP_FLOAT) && (in.type == PROP_RAW_FLOAT))) {
      /* avoid creating temporary buffer if the data type match */
      needconv = 0;
    }
    /* no item property pointer, can still be id property, or
     * property of a type derived from the collection pointer type */
    RNA_PROP_BEGIN (ptr, itemptr, prop) {
      if (itemptr.data) {
        if (itemprop) {
          /* we got the property already */
          iprop = itemprop;
        }
        else {
          /* not yet, look it up and verify if it is valid */
          iprop = RNA_struct_find_property(&itemptr, propname);

          if (iprop) {
            itemlen = rna_property_array_length_all_dimensions(&itemptr, iprop);
            itemtype = RNA_property_type(iprop);
          }
          else {
            BKE_reportf(reports, RPT_ERROR, "Property named '%s' not found", propname);
            err = 1;
            break;
          }

          if (!ELEM(itemtype, PROP_BOOLEAN, PROP_INT, PROP_FLOAT)) {
            BKE_report(reports, RPT_ERROR, "Only boolean, int and float properties supported");
            err = 1;
            break;
          }
        }

        /* editable check */
        if (!set || RNA_property_editable(&itemptr, iprop)) {
          if (a + itemlen > in.len) {
            BKE_reportf(
                reports, RPT_ERROR, "Array length mismatch (got %d, expected more)", in.len);
            err = 1;
            break;
          }

          if (itemlen == 0) {
            /* handle conversions */
            if (set) {
              switch (itemtype) {
                case PROP_BOOLEAN: {
                  int b;
                  RAW_GET(bool, b, in, a);
                  RNA_property_boolean_set(&itemptr, iprop, b);
                  break;
                }
                case PROP_INT: {
                  int i;
                  RAW_GET(int, i, in, a);
                  RNA_property_int_set(&itemptr, iprop, i);
                  break;
                }
                case PROP_FLOAT: {
                  float f;
                  RAW_GET(float, f, in, a);
                  RNA_property_float_set(&itemptr, iprop, f);
                  break;
                }
                default:
                  break;
              }
            }
            else {
              switch (itemtype) {
                case PROP_BOOLEAN: {
                  int b = RNA_property_boolean_get(&itemptr, iprop);
                  RAW_SET(bool, in, a, b);
                  break;
                }
                case PROP_INT: {
                  int i = RNA_property_int_get(&itemptr, iprop);
                  RAW_SET(int, in, a, i);
                  break;
                }
                case PROP_FLOAT: {
                  float f = RNA_property_float_get(&itemptr, iprop);
                  RAW_SET(float, in, a, f);
                  break;
                }
                default:
                  break;
              }
            }
            a++;
          }
          else if (needconv == 1) {
            /* allocate temporary array if needed */
            if (tmparray && tmplen != itemlen) {
              MEM_freeN(tmparray);
              tmparray = NULL;
            }
            if (!tmparray) {
              tmparray = MEM_callocN(sizeof(float) * itemlen, "RNA tmparray");
              tmplen = itemlen;
            }

            /* handle conversions */
            if (set) {
              switch (itemtype) {
                case PROP_BOOLEAN: {
                  for (j = 0; j < itemlen; j++, a++) {
                    RAW_GET(bool, ((bool *)tmparray)[j], in, a);
                  }
                  RNA_property_boolean_set_array(&itemptr, iprop, tmparray);
                  break;
                }
                case PROP_INT: {
                  for (j = 0; j < itemlen; j++, a++) {
                    RAW_GET(int, ((int *)tmparray)[j], in, a);
                  }
                  RNA_property_int_set_array(&itemptr, iprop, tmparray);
                  break;
                }
                case PROP_FLOAT: {
                  for (j = 0; j < itemlen; j++, a++) {
                    RAW_GET(float, ((float *)tmparray)[j], in, a);
                  }
                  RNA_property_float_set_array(&itemptr, iprop, tmparray);
                  break;
                }
                default:
                  break;
              }
            }
            else {
              switch (itemtype) {
                case PROP_BOOLEAN: {
                  RNA_property_boolean_get_array(&itemptr, iprop, tmparray);
                  for (j = 0; j < itemlen; j++, a++) {
                    RAW_SET(int, in, a, ((bool *)tmparray)[j]);
                  }
                  break;
                }
                case PROP_INT: {
                  RNA_property_int_get_array(&itemptr, iprop, tmparray);
                  for (j = 0; j < itemlen; j++, a++) {
                    RAW_SET(int, in, a, ((int *)tmparray)[j]);
                  }
                  break;
                }
                case PROP_FLOAT: {
                  RNA_property_float_get_array(&itemptr, iprop, tmparray);
                  for (j = 0; j < itemlen; j++, a++) {
                    RAW_SET(float, in, a, ((float *)tmparray)[j]);
                  }
                  break;
                }
                default:
                  break;
              }
            }
          }
          else {
            if (set) {
              switch (itemtype) {
                case PROP_BOOLEAN: {
                  RNA_property_boolean_set_array(&itemptr, iprop, &((bool *)in.array)[a]);
                  a += itemlen;
                  break;
                }
                case PROP_INT: {
                  RNA_property_int_set_array(&itemptr, iprop, &((int *)in.array)[a]);
                  a += itemlen;
                  break;
                }
                case PROP_FLOAT: {
                  RNA_property_float_set_array(&itemptr, iprop, &((float *)in.array)[a]);
                  a += itemlen;
                  break;
                }
                default:
                  break;
              }
            }
            else {
              switch (itemtype) {
                case PROP_BOOLEAN: {
                  RNA_property_boolean_get_array(&itemptr, iprop, &((bool *)in.array)[a]);
                  a += itemlen;
                  break;
                }
                case PROP_INT: {
                  RNA_property_int_get_array(&itemptr, iprop, &((int *)in.array)[a]);
                  a += itemlen;
                  break;
                }
                case PROP_FLOAT: {
                  RNA_property_float_get_array(&itemptr, iprop, &((float *)in.array)[a]);
                  a += itemlen;
                  break;
                }
                default:
                  break;
              }
            }
          }
        }
      }
    }
    RNA_PROP_END;

    if (tmparray) {
      MEM_freeN(tmparray);
    }

    return !err;
  }
}

RawPropertyType RNA_property_raw_type(PropertyRNA *prop)
{
  if (prop->rawtype == PROP_RAW_UNSET) {
    /* this property has no raw access,
     * yet we try to provide a raw type to help building the array. */
    switch (prop->type) {
      case PROP_BOOLEAN:
        return PROP_RAW_BOOLEAN;
      case PROP_INT:
        return PROP_RAW_INT;
      case PROP_FLOAT:
        return PROP_RAW_FLOAT;
      case PROP_ENUM:
        return PROP_RAW_INT;
      default:
        break;
    }
  }
  return prop->rawtype;
}

int RNA_property_collection_raw_get(ReportList *reports,
                                    PointerRNA *ptr,
                                    PropertyRNA *prop,
                                    const char *propname,
                                    void *array,
                                    RawPropertyType type,
                                    int len)
{
  return rna_raw_access(reports, ptr, prop, propname, array, type, len, 0);
}

int RNA_property_collection_raw_set(ReportList *reports,
                                    PointerRNA *ptr,
                                    PropertyRNA *prop,
                                    const char *propname,
                                    void *array,
                                    RawPropertyType type,
                                    int len)
{
  return rna_raw_access(reports, ptr, prop, propname, array, type, len, 1);
}

/* Standard iterator functions */

void rna_iterator_listbase_begin(CollectionPropertyIterator *iter,
                                 ListBase *lb,
                                 IteratorSkipFunc skip)
{
  ListBaseIterator *internal = &iter->internal.listbase;

  internal->link = (lb) ? lb->first : NULL;
  internal->skip = skip;

  iter->valid = (internal->link != NULL);

  if (skip && iter->valid && skip(iter, internal->link)) {
    rna_iterator_listbase_next(iter);
  }
}

void rna_iterator_listbase_next(CollectionPropertyIterator *iter)
{
  ListBaseIterator *internal = &iter->internal.listbase;

  if (internal->skip) {
    do {
      internal->link = internal->link->next;
      iter->valid = (internal->link != NULL);
    } while (iter->valid && internal->skip(iter, internal->link));
  }
  else {
    internal->link = internal->link->next;
    iter->valid = (internal->link != NULL);
  }
}

void *rna_iterator_listbase_get(CollectionPropertyIterator *iter)
{
  ListBaseIterator *internal = &iter->internal.listbase;

  return internal->link;
}

void rna_iterator_listbase_end(CollectionPropertyIterator *UNUSED(iter))
{
}

PointerRNA rna_listbase_lookup_int(PointerRNA *ptr,
                                   StructRNA *type,
                                   struct ListBase *lb,
                                   int index)
{
  void *data = BLI_findlink(lb, index);
  return rna_pointer_inherit_refine(ptr, type, data);
}

void rna_iterator_array_begin(CollectionPropertyIterator *iter,
                              void *ptr,
                              int itemsize,
                              int length,
                              bool free_ptr,
                              IteratorSkipFunc skip)
{
  ArrayIterator *internal;

  if (ptr == NULL) {
    length = 0;
  }
  else if (length == 0) {
    ptr = NULL;
    itemsize = 0;
  }

  internal = &iter->internal.array;
  internal->ptr = ptr;
  internal->free_ptr = free_ptr ? ptr : NULL;
  internal->endptr = ((char *)ptr) + length * itemsize;
  internal->itemsize = itemsize;
  internal->skip = skip;
  internal->length = length;

  iter->valid = (internal->ptr != internal->endptr);

  if (skip && iter->valid && skip(iter, internal->ptr)) {
    rna_iterator_array_next(iter);
  }
}

void rna_iterator_array_next(CollectionPropertyIterator *iter)
{
  ArrayIterator *internal = &iter->internal.array;

  if (internal->skip) {
    do {
      internal->ptr += internal->itemsize;
      iter->valid = (internal->ptr != internal->endptr);
    } while (iter->valid && internal->skip(iter, internal->ptr));
  }
  else {
    internal->ptr += internal->itemsize;
    iter->valid = (internal->ptr != internal->endptr);
  }
}

void *rna_iterator_array_get(CollectionPropertyIterator *iter)
{
  ArrayIterator *internal = &iter->internal.array;

  return internal->ptr;
}

void *rna_iterator_array_dereference_get(CollectionPropertyIterator *iter)
{
  ArrayIterator *internal = &iter->internal.array;

  /* for ** arrays */
  return *(void **)(internal->ptr);
}

void rna_iterator_array_end(CollectionPropertyIterator *iter)
{
  ArrayIterator *internal = &iter->internal.array;

  if (internal->free_ptr) {
    MEM_freeN(internal->free_ptr);
    internal->free_ptr = NULL;
  }
}

PointerRNA rna_array_lookup_int(
    PointerRNA *ptr, StructRNA *type, void *data, int itemsize, int length, int index)
{
  if (index < 0 || index >= length) {
    return PointerRNA_NULL;
  }

  return rna_pointer_inherit_refine(ptr, type, ((char *)data) + index * itemsize);
}

/* RNA Path - Experiment */

static char *rna_path_token(const char **path, char *fixedbuf, int fixedlen, int bracket)
{
  const char *p;
  char *buf;
  char quote = '\0';
  int i, j, len, escape;

  len = 0;

  if (bracket) {
    /* get data between [], check escaping ] with \] */
    if (**path == '[') {
      (*path)++;
    }
    else {
      return NULL;
    }

    p = *path;

    /* 2 kinds of lookups now, quoted or unquoted */
    quote = *p;

    if (quote != '"') { /* " - this comment is hack for Aligorith's text editor's sanity */
      quote = 0;
    }

    if (quote == 0) {
      while (*p && (*p != ']')) {
        len++;
        p++;
      }
    }
    else {
      escape = 0;
      /* skip the first quote */
      len++;
      p++;
      while (*p && (*p != quote || escape)) {
        escape = (*p == '\\');
        len++;
        p++;
      }

      /* skip the last quoted char to get the ']' */
      len++;
      p++;
    }

    if (*p != ']') {
      return NULL;
    }
  }
  else {
    /* get data until . or [ */
    p = *path;

    while (*p && *p != '.' && *p != '[') {
      len++;
      p++;
    }
  }

  /* empty, return */
  if (len == 0) {
    return NULL;
  }

  /* try to use fixed buffer if possible */
  if (len + 1 < fixedlen) {
    buf = fixedbuf;
  }
  else {
    buf = MEM_mallocN(sizeof(char) * (len + 1), "rna_path_token");
  }

  /* copy string, taking into account escaped ] */
  if (bracket) {
    for (p = *path, i = 0, j = 0; i < len; i++, p++) {
      if (*p == '\\' && *(p + 1) == quote) {
      }
      else {
        buf[j++] = *p;
      }
    }

    buf[j] = 0;
  }
  else {
    memcpy(buf, *path, sizeof(char) * len);
    buf[len] = '\0';
  }

  /* set path to start of next token */
  if (*p == ']') {
    p++;
  }
  if (*p == '.') {
    p++;
  }
  *path = p;

  return buf;
}

static int rna_token_strip_quotes(char *token)
{
  if (token[0] == '"') {
    int len = strlen(token);
    if (len >= 2 && token[len - 1] == '"') {
      /* strip away "" */
      token[len - 1] = '\0';
      return 1;
    }
  }
  return 0;
}

static bool rna_path_parse_collection_key(const char **path,
                                          PointerRNA *ptr,
                                          PropertyRNA *prop,
                                          PointerRNA *r_nextptr)
{
  char fixedbuf[256];
  int intkey;

  *r_nextptr = *ptr;

  /* end of path, ok */
  if (!(**path)) {
    return true;
  }

  if (**path == '[') {
    char *token;

    /* resolve the lookup with [] brackets */
    token = rna_path_token(path, fixedbuf, sizeof(fixedbuf), 1);

    if (!token) {
      return false;
    }

    /* check for "" to see if it is a string */
    if (rna_token_strip_quotes(token)) {
      if (RNA_property_collection_lookup_string(ptr, prop, token + 1, r_nextptr)) {
        /* pass */
      }
      else {
        r_nextptr->data = NULL;
      }
    }
    else {
      /* otherwise do int lookup */
      intkey = atoi(token);
      if (intkey == 0 && (token[0] != '0' || token[1] != '\0')) {
        return false; /* we can be sure the fixedbuf was used in this case */
      }
      if (RNA_property_collection_lookup_int(ptr, prop, intkey, r_nextptr)) {
        /* pass */
      }
      else {
        r_nextptr->data = NULL;
      }
    }

    if (token != fixedbuf) {
      MEM_freeN(token);
    }
  }
  else {
    if (RNA_property_collection_type_get(ptr, prop, r_nextptr)) {
      /* pass */
    }
    else {
      /* ensure we quit on invalid values */
      r_nextptr->data = NULL;
    }
  }

  return true;
}

static bool rna_path_parse_array_index(const char **path,
                                       PointerRNA *ptr,
                                       PropertyRNA *prop,
                                       int *r_index)
{
  char fixedbuf[256];
  int index_arr[RNA_MAX_ARRAY_DIMENSION] = {0};
  int len[RNA_MAX_ARRAY_DIMENSION];
  const int dim = RNA_property_array_dimension(ptr, prop, len);
  int i;

  *r_index = -1;

  /* end of path, ok */
  if (!(**path)) {
    return true;
  }

  for (i = 0; i < dim; i++) {
    int temp_index = -1;
    char *token;

    /* multi index resolve */
    if (**path == '[') {
      token = rna_path_token(path, fixedbuf, sizeof(fixedbuf), 1);

      if (token == NULL) {
        /* invalid syntax blah[] */
        return false;
      }
      /* check for "" to see if it is a string */
      else if (rna_token_strip_quotes(token)) {
        temp_index = RNA_property_array_item_index(prop, *(token + 1));
      }
      else {
        /* otherwise do int lookup */
        temp_index = atoi(token);

        if (temp_index == 0 && (token[0] != '0' || token[1] != '\0')) {
          if (token != fixedbuf) {
            MEM_freeN(token);
          }

          return false;
        }
      }
    }
    else if (dim == 1) {
      /* location.x || scale.X, single dimension arrays only */
      token = rna_path_token(path, fixedbuf, sizeof(fixedbuf), 0);
      if (token == NULL) {
        /* invalid syntax blah.. */
        return false;
      }
      temp_index = RNA_property_array_item_index(prop, *token);
    }
    else {
      /* just to avoid uninitialized pointer use */
      token = fixedbuf;
    }

    if (token != fixedbuf) {
      MEM_freeN(token);
    }

    /* out of range */
    if (temp_index < 0 || temp_index >= len[i]) {
      return false;
    }

    index_arr[i] = temp_index;
    /* end multi index resolve */
  }

  /* arrays always contain numbers so further values are not valid */
  if (**path) {
    return false;
  }

  /* flatten index over all dimensions */
  {
    int totdim = 1;
    int flat_index = 0;

    for (i = dim - 1; i >= 0; i--) {
      flat_index += index_arr[i] * totdim;
      totdim *= len[i];
    }

    *r_index = flat_index;
  }
  return true;
}

/**
 * Generic rna path parser.
 *
 * \note All parameters besides \a ptr and \a path are optional.
 *
 * \param ptr: The root of given RNA path.
 * \param path: The RNA path.
 * \param r_ptr: The final RNA data holding the last property in \a path.
 * \param r_prop: The final property of \a r_ptr, from \a path.
 * \param r_index: The final index in the \a r_prop, if defined by \a path.
 * \param r_item_ptr: Only valid for Pointer and Collection,
 * return the actual value of the pointer, or of the collection item.
 * Mutually exclusive with \a eval_pointer option.
 * \param r_elements: A list of \a PropertyElemRNA items
 * (pairs of \a PointerRNA, \a PropertyRNA that represent the whole given \a path).
 * \param eval_pointer: If \a true, and \a path leads to a Pointer property,
 * or an item in a Collection property,
 * \a r_ptr will be set to the value of that property, and \a r_prop will be NULL.
 * Mutually exclusive with \a r_item_ptr.
 * \return \a true on success, \a false if the path is somehow invalid.
 */
static bool rna_path_parse(PointerRNA *ptr,
                           const char *path,
                           PointerRNA *r_ptr,
                           PropertyRNA **r_prop,
                           int *r_index,
                           PointerRNA *r_item_ptr,
                           ListBase *r_elements,
                           const bool eval_pointer)
{
  BLI_assert(r_item_ptr == NULL || !eval_pointer);
  PropertyRNA *prop;
  PointerRNA curptr, nextptr;
  PropertyElemRNA *prop_elem = NULL;
  int index = -1;
  char fixedbuf[256];
  int type;
  const bool do_item_ptr = r_item_ptr != NULL && !eval_pointer;

  if (do_item_ptr) {
    RNA_POINTER_INVALIDATE(&nextptr);
  }

  prop = NULL;
  curptr = *ptr;

  if (path == NULL || *path == '\0') {
    return false;
  }

  while (*path) {
    if (do_item_ptr) {
      RNA_POINTER_INVALIDATE(&nextptr);
    }

    int use_id_prop = (*path == '[') ? 1 : 0;
    char *token;
    /* custom property lookup ?
     * C.object["someprop"]
     */

    if (!curptr.data) {
      return false;
    }

    /* look up property name in current struct */
    token = rna_path_token(&path, fixedbuf, sizeof(fixedbuf), use_id_prop);

    if (!token) {
      return false;
    }

    prop = NULL;
    if (use_id_prop) { /* look up property name in current struct */
      IDProperty *group = RNA_struct_idprops(&curptr, 0);
      if (group && rna_token_strip_quotes(token)) {
        prop = (PropertyRNA *)IDP_GetPropertyFromGroup(group, token + 1);
      }
    }
    else {
      prop = RNA_struct_find_property(&curptr, token);
    }

    if (token != fixedbuf) {
      MEM_freeN(token);
    }

    if (!prop) {
      return false;
    }

    if (r_elements) {
      prop_elem = MEM_mallocN(sizeof(PropertyElemRNA), __func__);
      prop_elem->ptr = curptr;
      prop_elem->prop = prop;
      prop_elem->index = -1; /* index will be added later, if needed. */
      BLI_addtail(r_elements, prop_elem);
    }

    type = RNA_property_type(prop);

    /* now look up the value of this property if it is a pointer or
     * collection, otherwise return the property rna so that the
     * caller can read the value of the property itself */
    switch (type) {
      case PROP_POINTER: {
        /* resolve pointer if further path elements follow
         * or explicitly requested
         */
        if (do_item_ptr || eval_pointer || *path != '\0') {
          nextptr = RNA_property_pointer_get(&curptr, prop);
        }

        if (eval_pointer || *path != '\0') {
          curptr = nextptr;
          prop = NULL; /* now we have a PointerRNA, the prop is our parent so forget it */
          index = -1;
        }
        break;
      }
      case PROP_COLLECTION: {
        /* Resolve pointer if further path elements follow.
         * Note that if path is empty, rna_path_parse_collection_key will do nothing anyway,
         * so do_item_ptr is of no use in that case.
         */
        if (*path) {
          if (!rna_path_parse_collection_key(&path, &curptr, prop, &nextptr)) {
            return false;
          }

          if (eval_pointer || *path != '\0') {
            curptr = nextptr;
            prop = NULL; /* now we have a PointerRNA, the prop is our parent so forget it */
            index = -1;
          }
        }
        break;
      }
      default:
        if (r_index || prop_elem) {
          if (!rna_path_parse_array_index(&path, &curptr, prop, &index)) {
            return false;
          }

          if (prop_elem) {
            prop_elem->index = index;
          }
        }
        break;
    }
  }

  if (r_ptr) {
    *r_ptr = curptr;
  }
  if (r_prop) {
    *r_prop = prop;
  }
  if (r_index) {
    *r_index = index;
  }
  if (r_item_ptr && do_item_ptr) {
    *r_item_ptr = nextptr;
  }

  if (prop_elem && (prop_elem->ptr.data != curptr.data || prop_elem->prop != prop ||
                    prop_elem->index != index)) {
    prop_elem = MEM_mallocN(sizeof(PropertyElemRNA), __func__);
    prop_elem->ptr = curptr;
    prop_elem->prop = prop;
    prop_elem->index = index;
    BLI_addtail(r_elements, prop_elem);
  }

  return true;
}

/**
 * Resolve the given RNA Path to find the pointer and/or property
 * indicated by fully resolving the path.
 *
 * \warning Unlike \a RNA_path_resolve_property(), that one *will* try to follow RNAPointers,
 * e.g. the path 'parent' applied to a RNAObject \a ptr will return the object.parent in \a r_ptr,
 * and a NULL \a r_prop...
 *
 * \note Assumes all pointers provided are valid
 * \return True if path can be resolved to a valid "pointer + property" OR "pointer only"
 */
bool RNA_path_resolve(PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop)
{
  if (!rna_path_parse(ptr, path, r_ptr, r_prop, NULL, NULL, NULL, true)) {
    return false;
  }

  return r_ptr->data != NULL;
}

/**
 * Resolve the given RNA Path to find the pointer and/or property + array index
 * indicated by fully resolving the path.
 *
 * \note Assumes all pointers provided are valid.
 * \return True if path can be resolved to a valid "pointer + property" OR "pointer only"
 */
bool RNA_path_resolve_full(
    PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index)
{
  if (!rna_path_parse(ptr, path, r_ptr, r_prop, r_index, NULL, NULL, true)) {
    return false;
  }

  return r_ptr->data != NULL;
}

/**
 * Resolve the given RNA Path to find both the pointer AND property
 * indicated by fully resolving the path.
 *
 * This is a convenience method to avoid logic errors and ugly syntax.
 * \note Assumes all pointers provided are valid
 * \return True only if both a valid pointer and property are found after resolving the path
 */
bool RNA_path_resolve_property(PointerRNA *ptr,
                               const char *path,
                               PointerRNA *r_ptr,
                               PropertyRNA **r_prop)
{
  if (!rna_path_parse(ptr, path, r_ptr, r_prop, NULL, NULL, NULL, false)) {
    return false;
  }

  return r_ptr->data != NULL && *r_prop != NULL;
}

/**
 * Resolve the given RNA Path to find the pointer AND property (as well as the array index)
 * indicated by fully resolving the path.
 *
 * This is a convenience method to avoid logic errors and ugly syntax.
 * \note Assumes all pointers provided are valid
 * \return True only if both a valid pointer and property are found after resolving the path
 */
bool RNA_path_resolve_property_full(
    PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index)
{
  if (!rna_path_parse(ptr, path, r_ptr, r_prop, r_index, NULL, NULL, false)) {
    return false;
  }

  return r_ptr->data != NULL && *r_prop != NULL;
}

/**
 * Resolve the given RNA Path to find both the pointer AND property
 * indicated by fully resolving the path, and get the value of the Pointer property
 * (or item of the collection).
 *
 * This is a convenience method to avoid logic errors and ugly syntax,
 * it combines both \a RNA_path_resolve and #RNA_path_resolve_property in a single call.
 * \note Assumes all pointers provided are valid.
 * \param r_item_pointer: The final Pointer or Collection item value.
 * You must check for its validity before use!
 * \return True only if both a valid pointer and property are found after resolving the path
 */
bool RNA_path_resolve_property_and_item_pointer(PointerRNA *ptr,
                                                const char *path,
                                                PointerRNA *r_ptr,
                                                PropertyRNA **r_prop,
                                                PointerRNA *r_item_ptr)
{
  if (!rna_path_parse(ptr, path, r_ptr, r_prop, NULL, r_item_ptr, NULL, false)) {
    return false;
  }

  return r_ptr->data != NULL && *r_prop != NULL;
}

/**
 * Resolve the given RNA Path to find both the pointer AND property (as well as the array index)
 * indicated by fully resolving the path,
 * and get the value of the Pointer property (or item of the collection).
 *
 * This is a convenience method to avoid logic errors and ugly syntax,
 * it combines both \a RNA_path_resolve_full and
 * \a RNA_path_resolve_property_full in a single call.
 * \note Assumes all pointers provided are valid.
 * \param r_item_pointer: The final Pointer or Collection item value.
 * You must check for its validity before use!
 * \return True only if both a valid pointer and property are found after resolving the path
 */
bool RNA_path_resolve_property_and_item_pointer_full(PointerRNA *ptr,
                                                     const char *path,
                                                     PointerRNA *r_ptr,
                                                     PropertyRNA **r_prop,
                                                     int *r_index,
                                                     PointerRNA *r_item_ptr)
{
  if (!rna_path_parse(ptr, path, r_ptr, r_prop, r_index, r_item_ptr, NULL, false)) {
    return false;
  }

  return r_ptr->data != NULL && *r_prop != NULL;
}
/**
 * Resolve the given RNA Path into a linked list of PropertyElemRNA's.
 *
 * To be used when complex operations over path are needed, like e.g. get relative paths,
 * to avoid too much string operations.
 *
 * \return True if there was no error while resolving the path
 * \note Assumes all pointers provided are valid
 */
bool RNA_path_resolve_elements(PointerRNA *ptr, const char *path, ListBase *r_elements)
{
  return rna_path_parse(ptr, path, NULL, NULL, NULL, NULL, r_elements, false);
}

char *RNA_path_append(
    const char *path, PointerRNA *UNUSED(ptr), PropertyRNA *prop, int intkey, const char *strkey)
{
  DynStr *dynstr;
  const char *s;
  char appendstr[128], *result;

  dynstr = BLI_dynstr_new();

  /* add .identifier */
  if (path) {
    BLI_dynstr_append(dynstr, path);
    if (*path) {
      BLI_dynstr_append(dynstr, ".");
    }
  }

  BLI_dynstr_append(dynstr, RNA_property_identifier(prop));

  if (RNA_property_type(prop) == PROP_COLLECTION) {
    /* add ["strkey"] or [intkey] */
    BLI_dynstr_append(dynstr, "[");

    if (strkey) {
      BLI_dynstr_append(dynstr, "\"");
      for (s = strkey; *s; s++) {
        if (*s == '[') {
          appendstr[0] = '\\';
          appendstr[1] = *s;
          appendstr[2] = 0;
        }
        else {
          appendstr[0] = *s;
          appendstr[1] = 0;
        }
        BLI_dynstr_append(dynstr, appendstr);
      }
      BLI_dynstr_append(dynstr, "\"");
    }
    else {
      BLI_snprintf(appendstr, sizeof(appendstr), "%d", intkey);
      BLI_dynstr_append(dynstr, appendstr);
    }

    BLI_dynstr_append(dynstr, "]");
  }

  result = BLI_dynstr_get_cstring(dynstr);
  BLI_dynstr_free(dynstr);

  return result;
}

char *RNA_path_back(const char *path)
{
  char fixedbuf[256];
  const char *previous, *current;
  char *result;
  int i;

  if (!path) {
    return NULL;
  }

  previous = NULL;
  current = path;

  /* parse token by token until the end, then we back up to the previous
   * position and strip of the next token to get the path one step back */
  while (*current) {
    char *token;

    token = rna_path_token(&current, fixedbuf, sizeof(fixedbuf), 0);

    if (!token) {
      return NULL;
    }
    if (token != fixedbuf) {
      MEM_freeN(token);
    }

    /* in case of collection we also need to strip off [] */
    token = rna_path_token(&current, fixedbuf, sizeof(fixedbuf), 1);
    if (token && token != fixedbuf) {
      MEM_freeN(token);
    }

    if (!*current) {
      break;
    }

    previous = current;
  }

  if (!previous) {
    return NULL;
  }

  /* copy and strip off last token */
  i = previous - path;
  result = BLI_strdup(path);

  if (i > 0 && result[i - 1] == '.') {
    i--;
  }
  result[i] = 0;

  return result;
}

/* generic path search func
 * if its needed this could also reference the IDProperty direct */
typedef struct IDP_Chain {
  struct IDP_Chain *up; /* parent member, reverse and set to child for path conversion. */

  const char *name;
  int index;

} IDP_Chain;

static char *rna_idp_path_create(IDP_Chain *child_link)
{
  DynStr *dynstr = BLI_dynstr_new();
  char *path;
  bool is_first = true;

  int tot = 0;
  IDP_Chain *link = child_link;

  /* reverse the list */
  IDP_Chain *link_prev;
  link_prev = NULL;
  while (link) {
    IDP_Chain *link_next = link->up;
    link->up = link_prev;
    link_prev = link;
    link = link_next;
    tot++;
  }

  for (link = link_prev; link; link = link->up) {
    /* pass */
    if (link->index >= 0) {
      BLI_dynstr_appendf(dynstr, is_first ? "%s[%d]" : ".%s[%d]", link->name, link->index);
    }
    else {
      BLI_dynstr_appendf(dynstr, is_first ? "%s" : ".%s", link->name);
    }

    is_first = false;
  }

  path = BLI_dynstr_get_cstring(dynstr);
  BLI_dynstr_free(dynstr);

  if (*path == '\0') {
    MEM_freeN(path);
    path = NULL;
  }

  return path;
}

static char *rna_idp_path(PointerRNA *ptr,
                          IDProperty *haystack,
                          IDProperty *needle,
                          IDP_Chain *parent_link)
{
  char *path = NULL;
  IDP_Chain link;

  IDProperty *iter;
  int i;

  BLI_assert(haystack->type == IDP_GROUP);

  link.up = parent_link;
  /* always set both name and index,
   * else a stale value might get used */
  link.name = NULL;
  link.index = -1;

  for (i = 0, iter = haystack->data.group.first; iter; iter = iter->next, i++) {
    if (needle == iter) { /* found! */
      link.name = iter->name;
      link.index = -1;
      path = rna_idp_path_create(&link);
      break;
    }
    else {
      if (iter->type == IDP_GROUP) {
        /* ensure this is RNA */
        PropertyRNA *prop = RNA_struct_find_property(ptr, iter->name);
        if (prop && prop->type == PROP_POINTER) {
          PointerRNA child_ptr = RNA_property_pointer_get(ptr, prop);
          link.name = iter->name;
          link.index = -1;
          if ((path = rna_idp_path(&child_ptr, iter, needle, &link))) {
            break;
          }
        }
      }
      else if (iter->type == IDP_IDPARRAY) {
        PropertyRNA *prop = RNA_struct_find_property(ptr, iter->name);
        if (prop && prop->type == PROP_COLLECTION) {
          IDProperty *array = IDP_IDPArray(iter);
          if (needle >= array && needle < (iter->len + array)) { /* found! */
            link.name = iter->name;
            link.index = (int)(needle - array);
            path = rna_idp_path_create(&link);
            break;
          }
          else {
            int j;
            link.name = iter->name;
            for (j = 0; j < iter->len; j++, array++) {
              PointerRNA child_ptr;
              if (RNA_property_collection_lookup_int(ptr, prop, j, &child_ptr)) {
                link.index = j;
                if ((path = rna_idp_path(&child_ptr, array, needle, &link))) {
                  break;
                }
              }
            }
            if (path) {
              break;
            }
          }
        }
      }
    }
  }

  return path;
}

/**
 * Find the path from the structure referenced by the pointer to the IDProperty object.
 *
 * \param ptr Reference to the object owning the custom property storage.
 * \param needle Custom property object to find.
 * \return Relative path or NULL.
 */
char *RNA_path_from_struct_to_idproperty(PointerRNA *ptr, IDProperty *needle)
{
  IDProperty *haystack = RNA_struct_idprops(ptr, false);

  if (haystack) { /* can fail when called on bones */
    return rna_idp_path(ptr, haystack, needle, NULL);
  }
  else {
    return NULL;
  }
}

static char *rna_path_from_ID_to_idpgroup(PointerRNA *ptr)
{
  PointerRNA id_ptr;

  BLI_assert(ptr->owner_id != NULL);

  /* TODO, Support Bones/PoseBones. no pointers stored to the bones from here, only the ID.
   *       See example in T25746.
   *       Unless this is added only way to find this is to also search
   *       all bones and pose bones of an armature or object.
   */
  RNA_id_pointer_create(ptr->owner_id, &id_ptr);

  return RNA_path_from_struct_to_idproperty(&id_ptr, ptr->data);
}

/**
 * Find the actual ID pointer and path from it to the given ID.
 *
 * \param id: ID reference to search the global owner for.
 * \param[out] r_path: Path from the real ID to the initial ID.
 * \return The ID pointer, or NULL in case of failure.
 */
ID *RNA_find_real_ID_and_path(Main *bmain, ID *id, const char **r_path)
{
  if (r_path) {
    *r_path = "";
  }

  if ((id != NULL) && (id->flag & LIB_PRIVATE_DATA)) {
    switch (GS(id->name)) {
      case ID_NT:
        if (r_path) {
          *r_path = "node_tree";
        }
        return BKE_node_tree_find_owner_ID(bmain, (bNodeTree *)id);
      case ID_GR:
        if (r_path) {
          *r_path = "collection";
        }
        return (ID *)BKE_collection_master_scene_search(bmain, (Collection *)id);

      default:
        return NULL;
    }
  }
  else {
    return id;
  }
}

static char *rna_prepend_real_ID_path(Main *bmain, ID *id, char *path, ID **r_real_id)
{
  if (r_real_id != NULL) {
    *r_real_id = NULL;
  }

  const char *prefix;
  ID *real_id = RNA_find_real_ID_and_path(bmain, id, &prefix);

  if (r_real_id != NULL) {
    *r_real_id = real_id;
  }

  if (path != NULL) {
    char *new_path = NULL;

    if (real_id) {
      if (prefix[0]) {
        new_path = BLI_sprintfN("%s%s%s", prefix, path[0] == '[' ? "" : ".", path);
      }
      else {
        return path;
      }
    }

    MEM_freeN(path);
    return new_path;
  }
  else {
    return prefix[0] != '\0' ? BLI_strdup(prefix) : NULL;
  }
}

char *RNA_path_from_ID_to_struct(PointerRNA *ptr)
{
  char *ptrpath = NULL;

  if (!ptr->owner_id || !ptr->data) {
    return NULL;
  }

  if (!RNA_struct_is_ID(ptr->type)) {
    if (ptr->type->path) {
      /* if type has a path to some ID, use it */
      ptrpath = ptr->type->path(ptr);
    }
    else if (ptr->type->nested && RNA_struct_is_ID(ptr->type->nested)) {
      PointerRNA parentptr;
      PropertyRNA *userprop;

      /* find the property in the struct we're nested in that references this struct, and
       * use its identifier as the first part of the path used...
       */
      RNA_id_pointer_create(ptr->owner_id, &parentptr);
      userprop = RNA_struct_find_nested(&parentptr, ptr->type);

      if (userprop) {
        ptrpath = BLI_strdup(RNA_property_identifier(userprop));
      }
      else {
        return NULL; /* can't do anything about this case yet... */
      }
    }
    else if (RNA_struct_is_a(ptr->type, &RNA_PropertyGroup)) {
      /* special case, easier to deal with here then in ptr->type->path() */
      return rna_path_from_ID_to_idpgroup(ptr);
    }
    else {
      return NULL;
    }
  }

  return ptrpath;
}

char *RNA_path_from_real_ID_to_struct(Main *bmain, PointerRNA *ptr, struct ID **r_real)
{
  char *path = RNA_path_from_ID_to_struct(ptr);

  /* NULL path is valid in that case, when given struct is an ID one... */
  return rna_prepend_real_ID_path(bmain, ptr->owner_id, path, r_real);
}

static void rna_path_array_multi_from_flat_index(const int dimsize[RNA_MAX_ARRAY_LENGTH],
                                                 const int totdims,
                                                 const int index_dim,
                                                 int index,
                                                 int r_index_multi[RNA_MAX_ARRAY_LENGTH])
{
  int dimsize_step[RNA_MAX_ARRAY_LENGTH + 1];
  int i = totdims - 1;
  dimsize_step[i + 1] = 1;
  dimsize_step[i] = dimsize[i];
  while (--i != -1) {
    dimsize_step[i] = dimsize[i] * dimsize_step[i + 1];
  }
  while (++i != index_dim) {
    int index_round = index / dimsize_step[i + 1];
    r_index_multi[i] = index_round;
    index -= (index_round * dimsize_step[i + 1]);
  }
  BLI_assert(index == 0);
}

static void rna_path_array_multi_string_from_flat_index(PointerRNA *ptr,
                                                        PropertyRNA *prop,
                                                        int index_dim,
                                                        int index,
                                                        char *index_str,
                                                        int index_str_len)
{
  int dimsize[RNA_MAX_ARRAY_LENGTH];
  int totdims = RNA_property_array_dimension(ptr, prop, dimsize);
  int index_multi[RNA_MAX_ARRAY_LENGTH];

  rna_path_array_multi_from_flat_index(dimsize, totdims, index_dim, index, index_multi);

  for (int i = 0, offset = 0; (i < index_dim) && (offset < index_str_len); i++) {
    offset += BLI_snprintf_rlen(
        &index_str[offset], index_str_len - offset, "[%d]", index_multi[i]);
  }
}

/**
 * \param index_dim: The dimension to show, 0 disables. 1 for 1d array, 2 for 2d. etc.
 * \param index: The *flattened* index to use when \a ``index_dim > 0``,
 * this is expanded when used with multi-dimensional arrays.
 */
char *RNA_path_from_ID_to_property_index(PointerRNA *ptr,
                                         PropertyRNA *prop,
                                         int index_dim,
                                         int index)
{
  const bool is_rna = (prop->magic == RNA_MAGIC);
  const char *propname;
  char *ptrpath, *path;

  if (!ptr->owner_id || !ptr->data) {
    return NULL;
  }

  /* path from ID to the struct holding this property */
  ptrpath = RNA_path_from_ID_to_struct(ptr);

  propname = RNA_property_identifier(prop);

  /* support indexing w/ multi-dimensional arrays */
  char index_str[RNA_MAX_ARRAY_LENGTH * 12 + 1];
  if (index_dim == 0) {
    index_str[0] = '\0';
  }
  else {
    rna_path_array_multi_string_from_flat_index(
        ptr, prop, index_dim, index, index_str, sizeof(index_str));
  }

  if (ptrpath) {
    if (is_rna) {
      path = BLI_sprintfN("%s.%s%s", ptrpath, propname, index_str);
    }
    else {
      char propname_esc[MAX_IDPROP_NAME * 2];
      BLI_strescape(propname_esc, propname, sizeof(propname_esc));
      path = BLI_sprintfN("%s[\"%s\"]%s", ptrpath, propname_esc, index_str);
    }
    MEM_freeN(ptrpath);
  }
  else if (RNA_struct_is_ID(ptr->type)) {
    if (is_rna) {
      path = BLI_sprintfN("%s%s", propname, index_str);
    }
    else {
      char propname_esc[MAX_IDPROP_NAME * 2];
      BLI_strescape(propname_esc, propname, sizeof(propname_esc));
      path = BLI_sprintfN("[\"%s\"]%s", propname_esc, index_str);
    }
  }
  else {
    path = NULL;
  }

  return path;
}

char *RNA_path_from_ID_to_property(PointerRNA *ptr, PropertyRNA *prop)
{
  return RNA_path_from_ID_to_property_index(ptr, prop, 0, -1);
}

char *RNA_path_from_real_ID_to_property_index(
    Main *bmain, PointerRNA *ptr, PropertyRNA *prop, int index_dim, int index, ID **r_real_id)
{
  char *path = RNA_path_from_ID_to_property_index(ptr, prop, index_dim, index);

  /* NULL path is always an error here, in that case do not return the 'fake ID from real ID' part
   * of the path either. */
  return path != NULL ? rna_prepend_real_ID_path(bmain, ptr->owner_id, path, r_real_id) : NULL;
}

/**
 * \return the path to given ptr/prop from the closest ancestor of given type,
 * if any (else return NULL).
 */
char *RNA_path_resolve_from_type_to_property(PointerRNA *ptr,
                                             PropertyRNA *prop,
                                             const StructRNA *type)
{
  /* Try to recursively find an "type"'d ancestor,
   * to handle situations where path from ID is not enough. */
  PointerRNA idptr;
  ListBase path_elems = {NULL};
  char *path = NULL;
  char *full_path = RNA_path_from_ID_to_property(ptr, prop);

  if (full_path == NULL) {
    return NULL;
  }

  RNA_id_pointer_create(ptr->owner_id, &idptr);

  if (RNA_path_resolve_elements(&idptr, full_path, &path_elems)) {
    PropertyElemRNA *prop_elem;

    for (prop_elem = path_elems.last; prop_elem; prop_elem = prop_elem->prev) {
      if (RNA_struct_is_a(prop_elem->ptr.type, type)) {
        char *ref_path = RNA_path_from_ID_to_struct(&prop_elem->ptr);
        if (ref_path) {
          path = BLI_strdup(full_path + strlen(ref_path) + 1); /* +1 for the linking '.' */
          MEM_freeN(ref_path);
        }
        break;
      }
    }

    BLI_freelistN(&path_elems);
  }

  MEM_freeN(full_path);
  return path;
}

/**
 * Get the ID as a python representation, eg:
 *   bpy.data.foo["bar"]
 */
char *RNA_path_full_ID_py(Main *bmain, ID *id)
{
  const char *path;
  ID *id_real = RNA_find_real_ID_and_path(bmain, id, &path);

  if (id_real) {
    id = id_real;
  }
  else {
    path = "";
  }

  char id_esc[(sizeof(id->name) - 2) * 2];

  BLI_strescape(id_esc, id->name + 2, sizeof(id_esc));

  return BLI_sprintfN("bpy.data.%s[\"%s\"]%s%s",
                      BKE_idcode_to_name_plural(GS(id->name)),
                      id_esc,
                      path[0] ? "." : "",
                      path);
}

/**
 * Get the ID.struct as a python representation, eg:
 *   bpy.data.foo["bar"].some_struct
 */
char *RNA_path_full_struct_py(Main *bmain, struct PointerRNA *ptr)
{
  char *id_path;
  char *data_path;

  char *ret;

  if (!ptr->owner_id) {
    return NULL;
  }

  /* never fails */
  id_path = RNA_path_full_ID_py(bmain, ptr->owner_id);

  data_path = RNA_path_from_ID_to_struct(ptr);

  /* XXX data_path may be NULL (see T36788),
   * do we want to get the 'bpy.data.foo["bar"].(null)' stuff? */
  ret = BLI_sprintfN("%s.%s", id_path, data_path);

  if (data_path) {
    MEM_freeN(data_path);
  }
  MEM_freeN(id_path);

  return ret;
}

/**
 * Get the ID.struct.property as a python representation, eg:
 *   bpy.data.foo["bar"].some_struct.some_prop[10]
 */
char *RNA_path_full_property_py_ex(
    Main *bmain, PointerRNA *ptr, PropertyRNA *prop, int index, bool use_fallback)
{
  char *id_path;
  const char *data_delim;
  const char *data_path;
  bool data_path_free;

  char *ret;

  if (!ptr->owner_id) {
    return NULL;
  }

  /* never fails */
  id_path = RNA_path_full_ID_py(bmain, ptr->owner_id);

  data_path = RNA_path_from_ID_to_property(ptr, prop);
  if (data_path) {
    data_delim = (data_path[0] == '[') ? "" : ".";
    data_path_free = true;
  }
  else {
    if (use_fallback) {
      /* fuzzy fallback. be explicit in our ignoranc. */
      data_path = RNA_property_identifier(prop);
      data_delim = " ... ";
    }
    else {
      data_delim = ".";
    }
    data_path_free = false;
  }

  if ((index == -1) || (RNA_property_array_check(prop) == false)) {
    ret = BLI_sprintfN("%s%s%s", id_path, data_delim, data_path);
  }
  else {
    ret = BLI_sprintfN("%s%s%s[%d]", id_path, data_delim, data_path, index);
  }
  MEM_freeN(id_path);
  if (data_path_free) {
    MEM_freeN((void *)data_path);
  }

  return ret;
}

char *RNA_path_full_property_py(Main *bmain, PointerRNA *ptr, PropertyRNA *prop, int index)
{
  return RNA_path_full_property_py_ex(bmain, ptr, prop, index, false);
}

/**
 * Get the struct.property as a python representation, eg:
 *   some_struct.some_prop[10]
 */
char *RNA_path_struct_property_py(PointerRNA *ptr, PropertyRNA *prop, int index)
{
  char *data_path;

  char *ret;

  if (!ptr->owner_id) {
    return NULL;
  }

  data_path = RNA_path_from_ID_to_property(ptr, prop);

  if (data_path == NULL) {
    /* this may not be an ID at all, check for simple when pointer owns property.
     * TODO, more complex nested case */
    if (!RNA_struct_is_ID(ptr->type)) {
      const char *prop_identifier = RNA_property_identifier(prop);
      if (RNA_struct_find_property(ptr, prop_identifier) == prop) {
        data_path = BLI_strdup(prop_identifier);
      }
    }
  }

  if ((index == -1) || (RNA_property_array_check(prop) == false)) {
    ret = BLI_sprintfN("%s", data_path);
  }
  else {
    ret = BLI_sprintfN("%s[%d]", data_path, index);
  }

  if (data_path) {
    MEM_freeN(data_path);
  }

  return ret;
}

/**
 * Get the struct.property as a python representation, eg:
 *   some_prop[10]
 */
char *RNA_path_property_py(PointerRNA *UNUSED(ptr), PropertyRNA *prop, int index)
{
  char *ret;

  if ((index == -1) || (RNA_property_array_check(prop) == false)) {
    ret = BLI_sprintfN("%s", RNA_property_identifier(prop));
  }
  else {
    ret = BLI_sprintfN("%s[%d]", RNA_property_identifier(prop), index);
  }

  return ret;
}

/* Quick name based property access */

bool RNA_boolean_get(PointerRNA *ptr, const char *name)
{
  PropertyRNA *prop = RNA_struct_find_property(ptr, name);

  if (prop) {
    return RNA_property_boolean_get(ptr, prop);
  }
  else {
    printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
    return 0;
  }
}

void RNA_boolean_set(PointerRNA *ptr, const char *name, bool value)
{
  PropertyRNA *prop = RNA_struct_find_property(ptr, name);

  if (prop) {
    RNA_property_boolean_set(ptr, prop, value);
  }
  else {
    printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
  }
}

void RNA_boolean_get_array(PointerRNA *ptr, const char *name, bool *values)
{
  PropertyRNA *prop = RNA_struct_find_property(ptr, name);

  if (prop) {
    RNA_property_boolean_get_array(ptr, prop, values);
  }
  else {
    printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
  }
}

void RNA_boolean_set_array(PointerRNA *ptr, const char *name, const bool *values)
{
  PropertyRNA *prop = RNA_struct_find_property(ptr, name);

  if (prop) {
    RNA_property_boolean_set_array(ptr, prop, values);
  }
  else {
    printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
  }
}

int RNA_int_get(PointerRNA *ptr, const char *name)
{
  PropertyRNA *prop = RNA_struct_find_property(ptr, name);

  if (prop) {
    return RNA_property_int_get(ptr, prop);
  }
  else {
    printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
    return 0;
  }
}

void RNA_int_set(PointerRNA *ptr, const char *name, int value)
{
  PropertyRNA *prop = RNA_struct_find_property(ptr, name);

  if (prop) {
    RNA_property_int_set(ptr, prop, value);
  }
  else {
    printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
  }
}

void RNA_int_get_array(PointerRNA *ptr, const char *name, int *values)
{
  PropertyRNA *prop = RNA_struct_find_property(ptr, name);

  if (prop) {
    RNA_property_int_get_array(ptr, prop, values);
  }
  else {
    printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
  }
}

void RNA_int_set_array(PointerRNA *ptr, const char *name, const int *values)
{
  PropertyRNA *prop = RNA_struct_find_property(ptr, name);

  if (prop) {
    RNA_property_int_set_array(ptr, prop, values);
  }
  else {
    printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
  }
}

float RNA_float_get(PointerRNA *ptr, const char *name)
{
  PropertyRNA *prop = RNA_struct_find_property(ptr, name);

  if (prop) {
    return RNA_property_float_get(ptr, prop);
  }
  else {
    printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
    return 0;
  }
}

void RNA_float_set(PointerRNA *ptr, const char *name, float value)
{
  PropertyRNA *prop = RNA_struct_find_property(ptr, name);

  if (prop) {
    RNA_property_float_set(ptr, prop, value);
  }
  else {
    printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
  }
}

void RNA_float_get_array(PointerRNA *ptr, const char *name, float *values)
{
  PropertyRNA *prop = RNA_struct_find_property(ptr, name);

  if (prop) {
    RNA_property_float_get_array(ptr, prop, values);
  }
  else {
    printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
  }
}

void RNA_float_set_array(PointerRNA *ptr, const char *name, const float *values)
{
  PropertyRNA *prop = RNA_struct_find_property(ptr, name);

  if (prop) {
    RNA_property_float_set_array(ptr, prop, values);
  }
  else {
    printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
  }
}

int RNA_enum_get(PointerRNA *ptr, const char *name)
{
  PropertyRNA *prop = RNA_struct_find_property(ptr, name);

  if (prop) {
    return RNA_property_enum_get(ptr, prop);
  }
  else {
    printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
    return 0;
  }
}

void RNA_enum_set(PointerRNA *ptr, const char *name, int value)
{
  PropertyRNA *prop = RNA_struct_find_property(ptr, name);

  if (prop) {
    RNA_property_enum_set(ptr, prop, value);
  }
  else {
    printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
  }
}

void RNA_enum_set_identifier(bContext *C, PointerRNA *ptr, const char *name, const char *id)
{
  PropertyRNA *prop = RNA_struct_find_property(ptr, name);

  if (prop) {
    int value;
    if (RNA_property_enum_value(C, ptr, prop, id, &value)) {
      RNA_property_enum_set(ptr, prop, value);
    }
    else {
      printf("%s: %s.%s has no enum id '%s'.\n", __func__, ptr->type->identifier, name, id);
    }
  }
  else {
    printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
  }
}

bool RNA_enum_is_equal(bContext *C, PointerRNA *ptr, const char *name, const char *enumname)
{
  PropertyRNA *prop = RNA_struct_find_property(ptr, name);
  const EnumPropertyItem *item;
  bool free;

  if (prop) {
    int i;
    bool cmp = false;

    RNA_property_enum_items(C, ptr, prop, &item, NULL, &free);
    i = RNA_enum_from_identifier(item, enumname);
    if (i != -1) {
      cmp = (item[i].value == RNA_property_enum_get(ptr, prop));
    }

    if (free) {
      MEM_freeN((void *)item);
    }

    if (i != -1) {
      return cmp;
    }

    printf("%s: %s.%s item %s not found.\n", __func__, ptr->type->identifier, name, enumname);
    return false;
  }
  else {
    printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
    return false;
  }
}

bool RNA_enum_value_from_id(const EnumPropertyItem *item, const char *identifier, int *r_value)
{
  const int i = RNA_enum_from_identifier(item, identifier);
  if (i != -1) {
    *r_value = item[i].value;
    return true;
  }
  else {
    return false;
  }
}

bool RNA_enum_id_from_value(const EnumPropertyItem *item, int value, const char **r_identifier)
{
  const int i = RNA_enum_from_value(item, value);
  if (i != -1) {
    *r_identifier = item[i].identifier;
    return true;
  }
  else {
    return false;
  }
}

bool RNA_enum_icon_from_value(const EnumPropertyItem *item, int value, int *r_icon)
{
  const int i = RNA_enum_from_value(item, value);
  if (i != -1) {
    *r_icon = item[i].icon;
    return true;
  }
  else {
    return false;
  }
}

bool RNA_enum_name_from_value(const EnumPropertyItem *item, int value, const char **r_name)
{
  const int i = RNA_enum_from_value(item, value);
  if (i != -1) {
    *r_name = item[i].name;
    return true;
  }
  else {
    return false;
  }
}

void RNA_string_get(PointerRNA *ptr, const char *name, char *value)
{
  PropertyRNA *prop = RNA_struct_find_property(ptr, name);

  if (prop) {
    RNA_property_string_get(ptr, prop, value);
  }
  else {
    printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
    value[0] = '\0';
  }
}

char *RNA_string_get_alloc(PointerRNA *ptr, const char *name, char *fixedbuf, int fixedlen)
{
  PropertyRNA *prop = RNA_struct_find_property(ptr, name);

  if (prop) {
    /* TODO, pass length */
    return RNA_property_string_get_alloc(ptr, prop, fixedbuf, fixedlen, NULL);
  }
  else {
    printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
    return NULL;
  }
}

int RNA_string_length(PointerRNA *ptr, const char *name)
{
  PropertyRNA *prop = RNA_struct_find_property(ptr, name);

  if (prop) {
    return RNA_property_string_length(ptr, prop);
  }
  else {
    printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
    return 0;
  }
}

void RNA_string_set(PointerRNA *ptr, const char *name, const char *value)
{
  PropertyRNA *prop = RNA_struct_find_property(ptr, name);

  if (prop) {
    RNA_property_string_set(ptr, prop, value);
  }
  else {
    printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
  }
}

PointerRNA RNA_pointer_get(PointerRNA *ptr, const char *name)
{
  PropertyRNA *prop = RNA_struct_find_property(ptr, name);

  if (prop) {
    return RNA_property_pointer_get(ptr, prop);
  }
  else {
    printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);

    return PointerRNA_NULL;
  }
}

void RNA_pointer_set(PointerRNA *ptr, const char *name, PointerRNA ptr_value)
{
  PropertyRNA *prop = RNA_struct_find_property(ptr, name);

  if (prop) {
    RNA_property_pointer_set(ptr, prop, ptr_value, NULL);
  }
  else {
    printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
  }
}

void RNA_pointer_add(PointerRNA *ptr, const char *name)
{
  PropertyRNA *prop = RNA_struct_find_property(ptr, name);

  if (prop) {
    RNA_property_pointer_add(ptr, prop);
  }
  else {
    printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
  }
}

void RNA_collection_begin(PointerRNA *ptr, const char *name, CollectionPropertyIterator *iter)
{
  PropertyRNA *prop = RNA_struct_find_property(ptr, name);

  if (prop) {
    RNA_property_collection_begin(ptr, prop, iter);
  }
  else {
    printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
  }
}

void RNA_collection_add(PointerRNA *ptr, const char *name, PointerRNA *r_value)
{
  PropertyRNA *prop = RNA_struct_find_property(ptr, name);

  if (prop) {
    RNA_property_collection_add(ptr, prop, r_value);
  }
  else {
    printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
  }
}

void RNA_collection_clear(PointerRNA *ptr, const char *name)
{
  PropertyRNA *prop = RNA_struct_find_property(ptr, name);

  if (prop) {
    RNA_property_collection_clear(ptr, prop);
  }
  else {
    printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
  }
}

int RNA_collection_length(PointerRNA *ptr, const char *name)
{
  PropertyRNA *prop = RNA_struct_find_property(ptr, name);

  if (prop) {
    return RNA_property_collection_length(ptr, prop);
  }
  else {
    printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
    return 0;
  }
}

bool RNA_property_is_set_ex(PointerRNA *ptr, PropertyRNA *prop, bool use_ghost)
{
  prop = rna_ensure_property(prop);
  if (prop->flag & PROP_IDPROPERTY) {
    IDProperty *idprop = rna_idproperty_find(ptr, prop->identifier);
    return ((idprop != NULL) && (use_ghost == false || !(idprop->flag & IDP_FLAG_GHOST)));
  }
  else {
    return true;
  }
}

bool RNA_property_is_set(PointerRNA *ptr, PropertyRNA *prop)
{
  prop = rna_ensure_property(prop);
  if (prop->flag & PROP_IDPROPERTY) {
    IDProperty *idprop = rna_idproperty_find(ptr, prop->identifier);
    return ((idprop != NULL) && !(idprop->flag & IDP_FLAG_GHOST));
  }
  else {
    return true;
  }
}

void RNA_property_unset(PointerRNA *ptr, PropertyRNA *prop)
{
  prop = rna_ensure_property(prop);
  if (prop->flag & PROP_IDPROPERTY) {
    rna_idproperty_free(ptr, prop->identifier);
  }
}

bool RNA_struct_property_is_set_ex(PointerRNA *ptr, const char *identifier, bool use_ghost)
{
  PropertyRNA *prop = RNA_struct_find_property(ptr, identifier);

  if (prop) {
    return RNA_property_is_set_ex(ptr, prop, use_ghost);
  }
  else {
    /* python raises an error */
    /* printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name); */
    return 0;
  }
}

bool RNA_struct_property_is_set(PointerRNA *ptr, const char *identifier)
{
  PropertyRNA *prop = RNA_struct_find_property(ptr, identifier);

  if (prop) {
    return RNA_property_is_set(ptr, prop);
  }
  else {
    /* python raises an error */
    /* printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name); */
    return 0;
  }
}

void RNA_struct_property_unset(PointerRNA *ptr, const char *identifier)
{
  PropertyRNA *prop = RNA_struct_find_property(ptr, identifier);

  if (prop) {
    RNA_property_unset(ptr, prop);
  }
}

bool RNA_property_is_idprop(const PropertyRNA *prop)
{
  return (prop->magic != RNA_MAGIC);
}

/* mainly for the UI */
bool RNA_property_is_unlink(PropertyRNA *prop)
{
  const int flag = RNA_property_flag(prop);
  if (RNA_property_type(prop) == PROP_STRING) {
    return (flag & PROP_NEVER_UNLINK) == 0;
  }
  else {
    return (flag & (PROP_NEVER_UNLINK | PROP_NEVER_NULL)) == 0;
  }
}

/* string representation of a property, python
 * compatible but can be used for display too,
 * context may be NULL */
char *RNA_pointer_as_string_id(bContext *C, PointerRNA *ptr)
{
  DynStr *dynstr = BLI_dynstr_new();
  char *cstring;

  const char *propname;
  int first_time = 1;

  BLI_dynstr_append(dynstr, "{");

  RNA_STRUCT_BEGIN (ptr, prop) {
    propname = RNA_property_identifier(prop);

    if (STREQ(propname, "rna_type")) {
      continue;
    }

    if (first_time == 0) {
      BLI_dynstr_append(dynstr, ", ");
    }
    first_time = 0;

    cstring = RNA_property_as_string(C, ptr, prop, -1, INT_MAX);
    BLI_dynstr_appendf(dynstr, "\"%s\":%s", propname, cstring);
    MEM_freeN(cstring);
  }
  RNA_STRUCT_END;

  BLI_dynstr_append(dynstr, "}");

  cstring = BLI_dynstr_get_cstring(dynstr);
  BLI_dynstr_free(dynstr);
  return cstring;
}

static char *rna_pointer_as_string__bldata(Main *bmain, PointerRNA *ptr)
{
  if (ptr->type == NULL || ptr->owner_id == NULL) {
    return BLI_strdup("None");
  }
  else if (RNA_struct_is_ID(ptr->type)) {
    return RNA_path_full_ID_py(bmain, ptr->owner_id);
  }
  else {
    return RNA_path_full_struct_py(bmain, ptr);
  }
}

char *RNA_pointer_as_string(bContext *C,
                            PointerRNA *ptr,
                            PropertyRNA *prop_ptr,
                            PointerRNA *ptr_prop)
{
  IDProperty *prop;
  if (ptr_prop->data == NULL) {
    return BLI_strdup("None");
  }
  else if ((prop = rna_idproperty_check(&prop_ptr, ptr)) && prop->type != IDP_ID) {
    return RNA_pointer_as_string_id(C, ptr_prop);
  }
  else {
    return rna_pointer_as_string__bldata(CTX_data_main(C), ptr_prop);
  }
}

/* context can be NULL */
char *RNA_pointer_as_string_keywords_ex(bContext *C,
                                        PointerRNA *ptr,
                                        const bool as_function,
                                        const bool all_args,
                                        const bool nested_args,
                                        const int max_prop_length,
                                        PropertyRNA *iterprop)
{
  const char *arg_name = NULL;

  PropertyRNA *prop;

  DynStr *dynstr = BLI_dynstr_new();
  char *cstring, *buf;
  bool first_iter = true;
  int flag, flag_parameter;

  RNA_PROP_BEGIN (ptr, propptr, iterprop) {
    prop = propptr.data;

    flag = RNA_property_flag(prop);
    flag_parameter = RNA_parameter_flag(prop);

    if (as_function && (flag_parameter & PARM_OUTPUT)) {
      continue;
    }

    arg_name = RNA_property_identifier(prop);

    if (STREQ(arg_name, "rna_type")) {
      continue;
    }

    if ((nested_args == false) && (RNA_property_type(prop) == PROP_POINTER)) {
      continue;
    }

    if (as_function && (prop->flag_parameter & PARM_REQUIRED)) {
      /* required args don't have useful defaults */
      BLI_dynstr_appendf(dynstr, first_iter ? "%s" : ", %s", arg_name);
      first_iter = false;
    }
    else {
      bool ok = true;

      if (all_args == true) {
        /* pass */
      }
      else if (RNA_struct_idprops_check(ptr->type)) {
        ok = RNA_property_is_set(ptr, prop);
      }

      if (ok) {
        if (as_function && RNA_property_type(prop) == PROP_POINTER) {
          /* don't expand pointers for functions */
          if (flag & PROP_NEVER_NULL) {
            /* we cant really do the right thing here. arg=arg?, hrmf! */
            buf = BLI_strdup(arg_name);
          }
          else {
            buf = BLI_strdup("None");
          }
        }
        else {
          buf = RNA_property_as_string(C, ptr, prop, -1, max_prop_length);
        }

        BLI_dynstr_appendf(dynstr, first_iter ? "%s=%s" : ", %s=%s", arg_name, buf);
        first_iter = false;
        MEM_freeN(buf);
      }
    }
  }
  RNA_PROP_END;

  cstring = BLI_dynstr_get_cstring(dynstr);
  BLI_dynstr_free(dynstr);
  return cstring;
}

char *RNA_pointer_as_string_keywords(bContext *C,
                                     PointerRNA *ptr,
                                     const bool as_function,
                                     const bool all_args,
                                     const bool nested_args,
                                     const int max_prop_length)
{
  PropertyRNA *iterprop;

  iterprop = RNA_struct_iterator_property(ptr->type);

  return RNA_pointer_as_string_keywords_ex(
      C, ptr, as_function, all_args, nested_args, max_prop_length, iterprop);
}

char *RNA_function_as_string_keywords(bContext *C,
                                      FunctionRNA *func,
                                      const bool as_function,
                                      const bool all_args,
                                      const int max_prop_length)
{
  PointerRNA funcptr;
  PropertyRNA *iterprop;

  RNA_pointer_create(NULL, &RNA_Function, func, &funcptr);

  iterprop = RNA_struct_find_property(&funcptr, "parameters");

  RNA_struct_iterator_property(funcptr.type);

  return RNA_pointer_as_string_keywords_ex(
      C, &funcptr, as_function, all_args, true, max_prop_length, iterprop);
}

static const char *bool_as_py_string(const int var)
{
  return var ? "True" : "False";
}

static void *rna_array_as_string_alloc(
    int type, int len, PointerRNA *ptr, PropertyRNA *prop, void **r_buf_end)
{
  void *buf_ret = NULL;
  if (type == PROP_BOOLEAN) {
    bool *buf = buf_ret = MEM_mallocN(sizeof(*buf) * len, __func__);
    RNA_property_boolean_get_array(ptr, prop, buf);
    *r_buf_end = buf + len;
  }
  else if (type == PROP_INT) {
    int *buf = buf_ret = MEM_mallocN(sizeof(*buf) * len, __func__);
    RNA_property_int_get_array(ptr, prop, buf);
    *r_buf_end = buf + len;
  }
  else if (type == PROP_FLOAT) {
    float *buf = buf_ret = MEM_mallocN(sizeof(*buf) * len, __func__);
    RNA_property_float_get_array(ptr, prop, buf);
    *r_buf_end = buf + len;
  }
  else {
    BLI_assert(0);
  }
  return buf_ret;
}

static void rna_array_as_string_elem(int type, void **buf_p, int len, DynStr *dynstr)
{
  /* This will print a comma separated string of the array elements from
   * buf start to len. We will add a comma if len == 1 to preserve tuples. */
  const int end = len - 1;
  if (type == PROP_BOOLEAN) {
    bool *buf = *buf_p;
    for (int i = 0; i < len; i++, buf++) {
      BLI_dynstr_appendf(dynstr, (i < end || !end) ? "%s, " : "%s", bool_as_py_string(*buf));
    }
    *buf_p = buf;
  }
  else if (type == PROP_INT) {
    int *buf = *buf_p;
    for (int i = 0; i < len; i++, buf++) {
      BLI_dynstr_appendf(dynstr, (i < end || !end) ? "%d, " : "%d", *buf);
    }
    *buf_p = buf;
  }
  else if (type == PROP_FLOAT) {
    float *buf = *buf_p;
    for (int i = 0; i < len; i++, buf++) {
      BLI_dynstr_appendf(dynstr, (i < end || !end) ? "%g, " : "%g", *buf);
    }
    *buf_p = buf;
  }
  else {
    BLI_assert(0);
  }
}

static void rna_array_as_string_recursive(
    int type, void **buf_p, int totdim, const int *dim_size, DynStr *dynstr)
{
  BLI_dynstr_append(dynstr, "(");
  if (totdim > 1) {
    totdim--;
    const int end = dim_size[totdim] - 1;
    for (int i = 0; i <= end; i++) {
      rna_array_as_string_recursive(type, buf_p, totdim, dim_size, dynstr);
      if (i < end || !end) {
        BLI_dynstr_append(dynstr, ", ");
      }
    }
  }
  else {
    rna_array_as_string_elem(type, buf_p, dim_size[0], dynstr);
  }
  BLI_dynstr_append(dynstr, ")");
}

static void rna_array_as_string(
    int type, int len, PointerRNA *ptr, PropertyRNA *prop, DynStr *dynstr)
{
  void *buf_end;
  void *buf = rna_array_as_string_alloc(type, len, ptr, prop, &buf_end);
  void *buf_step = buf;
  int totdim, dim_size[RNA_MAX_ARRAY_DIMENSION];

  totdim = RNA_property_array_dimension(ptr, prop, dim_size);

  rna_array_as_string_recursive(type, &buf_step, totdim, dim_size, dynstr);
  BLI_assert(buf_step == buf_end);
  MEM_freeN(buf);
}

char *RNA_property_as_string(
    bContext *C, PointerRNA *ptr, PropertyRNA *prop, int index, int max_prop_length)
{
  int type = RNA_property_type(prop);
  int len = RNA_property_array_length(ptr, prop);

  DynStr *dynstr = BLI_dynstr_new();
  char *cstring;

  /* see if we can coerce into a python type - PropertyType */
  switch (type) {
    case PROP_BOOLEAN:
      if (len == 0) {
        BLI_dynstr_append(dynstr, bool_as_py_string(RNA_property_boolean_get(ptr, prop)));
      }
      else {
        if (index != -1) {
          BLI_dynstr_append(dynstr,
                            bool_as_py_string(RNA_property_boolean_get_index(ptr, prop, index)));
        }
        else {
          rna_array_as_string(type, len, ptr, prop, dynstr);
        }
      }
      break;
    case PROP_INT:
      if (len == 0) {
        BLI_dynstr_appendf(dynstr, "%d", RNA_property_int_get(ptr, prop));
      }
      else {
        if (index != -1) {
          BLI_dynstr_appendf(dynstr, "%d", RNA_property_int_get_index(ptr, prop, index));
        }
        else {
          rna_array_as_string(type, len, ptr, prop, dynstr);
        }
      }
      break;
    case PROP_FLOAT:
      if (len == 0) {
        BLI_dynstr_appendf(dynstr, "%g", RNA_property_float_get(ptr, prop));
      }
      else {
        if (index != -1) {
          BLI_dynstr_appendf(dynstr, "%g", RNA_property_float_get_index(ptr, prop, index));
        }
        else {
          rna_array_as_string(type, len, ptr, prop, dynstr);
        }
      }
      break;
    case PROP_STRING: {
      char *buf_esc;
      char *buf;
      int length;

      length = RNA_property_string_length(ptr, prop);
      buf = MEM_mallocN(sizeof(char) * (length + 1), "RNA_property_as_string");
      buf_esc = MEM_mallocN(sizeof(char) * (length * 2 + 1), "RNA_property_as_string esc");
      RNA_property_string_get(ptr, prop, buf);
      BLI_strescape(buf_esc, buf, length * 2 + 1);
      MEM_freeN(buf);
      BLI_dynstr_appendf(dynstr, "\"%s\"", buf_esc);
      MEM_freeN(buf_esc);
      break;
    }
    case PROP_ENUM: {
      /* string arrays don't exist */
      const char *identifier;
      int val = RNA_property_enum_get(ptr, prop);

      if (RNA_property_flag(prop) & PROP_ENUM_FLAG) {
        /* represent as a python set */
        if (val) {
          const EnumPropertyItem *item_array;
          bool free;

          BLI_dynstr_append(dynstr, "{");

          RNA_property_enum_items(C, ptr, prop, &item_array, NULL, &free);
          if (item_array) {
            const EnumPropertyItem *item = item_array;
            bool is_first = true;
            for (; item->identifier; item++) {
              if (item->identifier[0] && item->value & val) {
                BLI_dynstr_appendf(dynstr, is_first ? "'%s'" : ", '%s'", item->identifier);
                is_first = false;
              }
            }

            if (free) {
              MEM_freeN((void *)item_array);
            }
          }

          BLI_dynstr_append(dynstr, "}");
        }
        else {
          /* annoying exception, don't confuse with dictionary syntax above: {} */
          BLI_dynstr_append(dynstr, "set()");
        }
      }
      else if (RNA_property_enum_identifier(C, ptr, prop, val, &identifier)) {
        BLI_dynstr_appendf(dynstr, "'%s'", identifier);
      }
      else {
        BLI_dynstr_append(dynstr, "'<UNKNOWN ENUM>'");
      }
      break;
    }
    case PROP_POINTER: {
      PointerRNA tptr = RNA_property_pointer_get(ptr, prop);
      cstring = RNA_pointer_as_string(C, ptr, prop, &tptr);
      BLI_dynstr_append(dynstr, cstring);
      MEM_freeN(cstring);
      break;
    }
    case PROP_COLLECTION: {
      int i = 0;
      CollectionPropertyIterator collect_iter;
      BLI_dynstr_append(dynstr, "[");

      for (RNA_property_collection_begin(ptr, prop, &collect_iter);
           (i < max_prop_length) && collect_iter.valid;
           RNA_property_collection_next(&collect_iter), i++) {
        PointerRNA itemptr = collect_iter.ptr;

        if (i != 0) {
          BLI_dynstr_append(dynstr, ", ");
        }

        /* now get every prop of the collection */
        cstring = RNA_pointer_as_string(C, ptr, prop, &itemptr);
        BLI_dynstr_append(dynstr, cstring);
        MEM_freeN(cstring);
      }

      RNA_property_collection_end(&collect_iter);
      BLI_dynstr_append(dynstr, "]");
      break;
    }
    default:
      BLI_dynstr_append(dynstr, "'<UNKNOWN TYPE>'"); /* TODO */
      break;
  }

  cstring = BLI_dynstr_get_cstring(dynstr);
  BLI_dynstr_free(dynstr);
  return cstring;
}

/* Function */

const char *RNA_function_identifier(FunctionRNA *func)
{
  return func->identifier;
}

const char *RNA_function_ui_description(FunctionRNA *func)
{
  return TIP_(func->description);
}

const char *RNA_function_ui_description_raw(FunctionRNA *func)
{
  return func->description;
}

int RNA_function_flag(FunctionRNA *func)
{
  return func->flag;
}

int RNA_function_defined(FunctionRNA *func)
{
  return func->call != NULL;
}

PropertyRNA *RNA_function_get_parameter(PointerRNA *UNUSED(ptr), FunctionRNA *func, int index)
{
  return BLI_findlink(&func->cont.properties, index);
}

PropertyRNA *RNA_function_find_parameter(PointerRNA *UNUSED(ptr),
                                         FunctionRNA *func,
                                         const char *identifier)
{
  PropertyRNA *parm;

  parm = func->cont.properties.first;
  for (; parm; parm = parm->next) {
    if (STREQ(RNA_property_identifier(parm), identifier)) {
      break;
    }
  }

  return parm;
}

const ListBase *RNA_function_defined_parameters(FunctionRNA *func)
{
  return &func->cont.properties;
}

/* Utility */

int RNA_parameter_flag(PropertyRNA *prop)
{
  return (int)rna_ensure_property(prop)->flag_parameter;
}

ParameterList *RNA_parameter_list_create(ParameterList *parms,
                                         PointerRNA *UNUSED(ptr),
                                         FunctionRNA *func)
{
  PropertyRNA *parm;
  PointerRNA null_ptr = PointerRNA_NULL;
  void *data;
  int alloc_size = 0, size;

  parms->arg_count = 0;
  parms->ret_count = 0;

  /* allocate data */
  for (parm = func->cont.properties.first; parm; parm = parm->next) {
    alloc_size += rna_parameter_size(parm);

    if (parm->flag_parameter & PARM_OUTPUT) {
      parms->ret_count++;
    }
    else {
      parms->arg_count++;
    }
  }

  parms->data = MEM_callocN(alloc_size, "RNA_parameter_list_create");
  parms->func = func;
  parms->alloc_size = alloc_size;

  /* set default values */
  data = parms->data;

  for (parm = func->cont.properties.first; parm; parm = parm->next) {
    size = rna_parameter_size(parm);

    /* set length to 0, these need to be set later, see bpy_array.c's py_to_array */
    if (parm->flag & PROP_DYNAMIC) {
      ParameterDynAlloc *data_alloc = data;
      data_alloc->array_tot = 0;
      data_alloc->array = NULL;
    }

    if (!(parm->flag_parameter & PARM_REQUIRED) && !(parm->flag & PROP_DYNAMIC)) {
      switch (parm->type) {
        case PROP_BOOLEAN:
          if (parm->arraydimension) {
            rna_property_boolean_get_default_array_values(
                &null_ptr, (BoolPropertyRNA *)parm, data);
          }
          else {
            memcpy(data, &((BoolPropertyRNA *)parm)->defaultvalue, size);
          }
          break;
        case PROP_INT:
          if (parm->arraydimension) {
            rna_property_int_get_default_array_values(&null_ptr, (IntPropertyRNA *)parm, data);
          }
          else {
            memcpy(data, &((IntPropertyRNA *)parm)->defaultvalue, size);
          }
          break;
        case PROP_FLOAT:
          if (parm->arraydimension) {
            rna_property_float_get_default_array_values(&null_ptr, (FloatPropertyRNA *)parm, data);
          }
          else {
            memcpy(data, &((FloatPropertyRNA *)parm)->defaultvalue, size);
          }
          break;
        case PROP_ENUM:
          memcpy(data, &((EnumPropertyRNA *)parm)->defaultvalue, size);
          break;
        case PROP_STRING: {
          const char *defvalue = ((StringPropertyRNA *)parm)->defaultvalue;
          if (defvalue && defvalue[0]) {
            /* causes bug [#29988], possibly this is only correct for thick wrapped
             * need to look further into it - campbell */
#if 0
            BLI_strncpy(data, defvalue, size);
#else
            memcpy(data, &defvalue, size);
#endif
          }
          break;
        }
        case PROP_POINTER:
        case PROP_COLLECTION:
          break;
      }
    }

    data = ((char *)data) + rna_parameter_size(parm);
  }

  return parms;
}

void RNA_parameter_list_free(ParameterList *parms)
{
  PropertyRNA *parm;
  int tot;

  parm = parms->func->cont.properties.first;
  for (tot = 0; parm; parm = parm->next) {
    if (parm->type == PROP_COLLECTION) {
      BLI_freelistN((ListBase *)((char *)parms->data + tot));
    }
    else if (parm->flag & PROP_DYNAMIC) {
      /* for dynamic arrays and strings, data is a pointer to an array */
      ParameterDynAlloc *data_alloc = (void *)(((char *)parms->data) + tot);
      if (data_alloc->array) {
        MEM_freeN(data_alloc->array);
      }
    }

    tot += rna_parameter_size(parm);
  }

  MEM_freeN(parms->data);
  parms->data = NULL;

  parms->func = NULL;
}

int RNA_parameter_list_size(ParameterList *parms)
{
  return parms->alloc_size;
}

int RNA_parameter_list_arg_count(ParameterList *parms)
{
  return parms->arg_count;
}

int RNA_parameter_list_ret_count(ParameterList *parms)
{
  return parms->ret_count;
}

void RNA_parameter_list_begin(ParameterList *parms, ParameterIterator *iter)
{
  /* may be useful but unused now */
  /* RNA_pointer_create(NULL, &RNA_Function, parms->func, &iter->funcptr); */ /*UNUSED*/

  iter->parms = parms;
  iter->parm = parms->func->cont.properties.first;
  iter->valid = iter->parm != NULL;
  iter->offset = 0;

  if (iter->valid) {
    iter->size = rna_parameter_size(iter->parm);
    iter->data = (((char *)iter->parms->data)); /* +iter->offset, always 0 */
  }
}

void RNA_parameter_list_next(ParameterIterator *iter)
{
  iter->offset += iter->size;
  iter->parm = iter->parm->next;
  iter->valid = iter->parm != NULL;

  if (iter->valid) {
    iter->size = rna_parameter_size(iter->parm);
    iter->data = (((char *)iter->parms->data) + iter->offset);
  }
}

void RNA_parameter_list_end(ParameterIterator *UNUSED(iter))
{
  /* nothing to do */
}

void RNA_parameter_get(ParameterList *parms, PropertyRNA *parm, void **value)
{
  ParameterIterator iter;

  RNA_parameter_list_begin(parms, &iter);

  for (; iter.valid; RNA_parameter_list_next(&iter)) {
    if (iter.parm == parm) {
      break;
    }
  }

  if (iter.valid) {
    if (parm->flag & PROP_DYNAMIC) {
      /* for dynamic arrays and strings, data is a pointer to an array */
      ParameterDynAlloc *data_alloc = iter.data;
      *value = data_alloc->array;
    }
    else {
      *value = iter.data;
    }
  }
  else {
    *value = NULL;
  }

  RNA_parameter_list_end(&iter);
}

void RNA_parameter_get_lookup(ParameterList *parms, const char *identifier, void **value)
{
  PropertyRNA *parm;

  parm = parms->func->cont.properties.first;
  for (; parm; parm = parm->next) {
    if (STREQ(RNA_property_identifier(parm), identifier)) {
      break;
    }
  }

  if (parm) {
    RNA_parameter_get(parms, parm, value);
  }
}

void RNA_parameter_set(ParameterList *parms, PropertyRNA *parm, const void *value)
{
  ParameterIterator iter;

  RNA_parameter_list_begin(parms, &iter);

  for (; iter.valid; RNA_parameter_list_next(&iter)) {
    if (iter.parm == parm) {
      break;
    }
  }

  if (iter.valid) {
    if (parm->flag & PROP_DYNAMIC) {
      /* for dynamic arrays and strings, data is a pointer to an array */
      ParameterDynAlloc *data_alloc = iter.data;
      size_t size = 0;
      switch (parm->type) {
        case PROP_STRING:
          size = sizeof(char);
          break;
        case PROP_INT:
        case PROP_BOOLEAN:
          size = sizeof(int);
          break;
        case PROP_FLOAT:
          size = sizeof(float);
          break;
        default:
          break;
      }
      size *= data_alloc->array_tot;
      if (data_alloc->array) {
        MEM_freeN(data_alloc->array);
      }
      data_alloc->array = MEM_mallocN(size, __func__);
      memcpy(data_alloc->array, value, size);
    }
    else {
      memcpy(iter.data, value, iter.size);
    }
  }

  RNA_parameter_list_end(&iter);
}

void RNA_parameter_set_lookup(ParameterList *parms, const char *identifier, const void *value)
{
  PropertyRNA *parm;

  parm = parms->func->cont.properties.first;
  for (; parm; parm = parm->next) {
    if (STREQ(RNA_property_identifier(parm), identifier)) {
      break;
    }
  }

  if (parm) {
    RNA_parameter_set(parms, parm, value);
  }
}

int RNA_parameter_dynamic_length_get(ParameterList *parms, PropertyRNA *parm)
{
  ParameterIterator iter;
  int len = 0;

  RNA_parameter_list_begin(parms, &iter);

  for (; iter.valid; RNA_parameter_list_next(&iter)) {
    if (iter.parm == parm) {
      break;
    }
  }

  if (iter.valid) {
    len = RNA_parameter_dynamic_length_get_data(parms, parm, iter.data);
  }

  RNA_parameter_list_end(&iter);

  return len;
}

void RNA_parameter_dynamic_length_set(ParameterList *parms, PropertyRNA *parm, int length)
{
  ParameterIterator iter;

  RNA_parameter_list_begin(parms, &iter);

  for (; iter.valid; RNA_parameter_list_next(&iter)) {
    if (iter.parm == parm) {
      break;
    }
  }

  if (iter.valid) {
    RNA_parameter_dynamic_length_set_data(parms, parm, iter.data, length);
  }

  RNA_parameter_list_end(&iter);
}

int RNA_parameter_dynamic_length_get_data(ParameterList *UNUSED(parms),
                                          PropertyRNA *parm,
                                          void *data)
{
  if (parm->flag & PROP_DYNAMIC) {
    return (int)((ParameterDynAlloc *)data)->array_tot;
  }
  return 0;
}

void RNA_parameter_dynamic_length_set_data(ParameterList *UNUSED(parms),
                                           PropertyRNA *parm,
                                           void *data,
                                           int length)
{
  if (parm->flag & PROP_DYNAMIC) {
    ((ParameterDynAlloc *)data)->array_tot = (intptr_t)length;
  }
}

int RNA_function_call(
    bContext *C, ReportList *reports, PointerRNA *ptr, FunctionRNA *func, ParameterList *parms)
{
  if (func->call) {
    func->call(C, reports, ptr, parms);

    return 0;
  }

  return -1;
}

int RNA_function_call_lookup(bContext *C,
                             ReportList *reports,
                             PointerRNA *ptr,
                             const char *identifier,
                             ParameterList *parms)
{
  FunctionRNA *func;

  func = RNA_struct_find_function(ptr->type, identifier);

  if (func) {
    return RNA_function_call(C, reports, ptr, func, parms);
  }

  return -1;
}

int RNA_function_call_direct(
    bContext *C, ReportList *reports, PointerRNA *ptr, FunctionRNA *func, const char *format, ...)
{
  va_list args;
  int ret;

  va_start(args, format);

  ret = RNA_function_call_direct_va(C, reports, ptr, func, format, args);

  va_end(args);

  return ret;
}

int RNA_function_call_direct_lookup(bContext *C,
                                    ReportList *reports,
                                    PointerRNA *ptr,
                                    const char *identifier,
                                    const char *format,
                                    ...)
{
  FunctionRNA *func;

  func = RNA_struct_find_function(ptr->type, identifier);

  if (func) {
    va_list args;
    int ret;

    va_start(args, format);

    ret = RNA_function_call_direct_va(C, reports, ptr, func, format, args);

    va_end(args);

    return ret;
  }

  return -1;
}

static int rna_function_format_array_length(const char *format, int ofs, int flen)
{
  char lenbuf[16];
  int idx = 0;

  if (format[ofs++] == '[') {
    for (; ofs < flen && format[ofs] != ']' && idx < sizeof(lenbuf) - 1; idx++, ofs++) {
      lenbuf[idx] = format[ofs];
    }
  }

  if (ofs < flen && format[ofs + 1] == ']') {
    /* XXX put better error reporting for (ofs >= flen) or idx over lenbuf capacity */
    lenbuf[idx] = '\0';
    return atoi(lenbuf);
  }

  return 0;
}

static int rna_function_parameter_parse(PointerRNA *ptr,
                                        PropertyRNA *prop,
                                        PropertyType type,
                                        char ftype,
                                        int len,
                                        void *dest,
                                        const void *src,
                                        StructRNA *srna,
                                        const char *tid,
                                        const char *fid,
                                        const char *pid)
{
  /* ptr is always a function pointer, prop always a parameter */

  switch (type) {
    case PROP_BOOLEAN: {
      if (ftype != 'b') {
        fprintf(
            stderr, "%s.%s: wrong type for parameter %s, a boolean was expected\n", tid, fid, pid);
        return -1;
      }

      if (len == 0) {
        *((bool *)dest) = *((bool *)src);
      }
      else {
        memcpy(dest, src, len * sizeof(bool));
      }

      break;
    }
    case PROP_INT: {
      if (ftype != 'i') {
        fprintf(stderr,
                "%s.%s: wrong type for parameter %s, an integer was expected\n",
                tid,
                fid,
                pid);
        return -1;
      }

      if (len == 0) {
        *((int *)dest) = *((int *)src);
      }
      else {
        memcpy(dest, src, len * sizeof(int));
      }

      break;
    }
    case PROP_FLOAT: {
      if (ftype != 'f') {
        fprintf(
            stderr, "%s.%s: wrong type for parameter %s, a float was expected\n", tid, fid, pid);
        return -1;
      }

      if (len == 0) {
        *((float *)dest) = *((float *)src);
      }
      else {
        memcpy(dest, src, len * sizeof(float));
      }

      break;
    }
    case PROP_STRING: {
      if (ftype != 's') {
        fprintf(
            stderr, "%s.%s: wrong type for parameter %s, a string was expected\n", tid, fid, pid);
        return -1;
      }

      *((char **)dest) = *((char **)src);

      break;
    }
    case PROP_ENUM: {
      if (ftype != 'e') {
        fprintf(
            stderr, "%s.%s: wrong type for parameter %s, an enum was expected\n", tid, fid, pid);
        return -1;
      }

      *((int *)dest) = *((int *)src);

      break;
    }
    case PROP_POINTER: {
      StructRNA *ptype;

      if (ftype != 'O') {
        fprintf(
            stderr, "%s.%s: wrong type for parameter %s, an object was expected\n", tid, fid, pid);
        return -1;
      }

      ptype = RNA_property_pointer_type(ptr, prop);

      if (prop->flag_parameter & PARM_RNAPTR) {
        *((PointerRNA *)dest) = *((PointerRNA *)src);
        break;
      }

      if (ptype != srna && !RNA_struct_is_a(srna, ptype)) {
        fprintf(stderr,
                "%s.%s: wrong type for parameter %s, "
                "an object of type %s was expected, passed an object of type %s\n",
                tid,
                fid,
                pid,
                RNA_struct_identifier(ptype),
                RNA_struct_identifier(srna));
        return -1;
      }

      *((void **)dest) = *((void **)src);

      break;
    }
    case PROP_COLLECTION: {
      StructRNA *ptype;
      ListBase *lb, *clb;
      Link *link;
      CollectionPointerLink *clink;

      if (ftype != 'C') {
        fprintf(stderr,
                "%s.%s: wrong type for parameter %s, a collection was expected\n",
                tid,
                fid,
                pid);
        return -1;
      }

      lb = (ListBase *)src;
      clb = (ListBase *)dest;
      ptype = RNA_property_pointer_type(ptr, prop);

      if (ptype != srna && !RNA_struct_is_a(srna, ptype)) {
        fprintf(stderr,
                "%s.%s: wrong type for parameter %s, "
                "a collection of objects of type %s was expected, "
                "passed a collection of objects of type %s\n",
                tid,
                fid,
                pid,
                RNA_struct_identifier(ptype),
                RNA_struct_identifier(srna));
        return -1;
      }

      for (link = lb->first; link; link = link->next) {
        clink = MEM_callocN(sizeof(CollectionPointerLink), "CCollectionPointerLink");
        RNA_pointer_create(NULL, srna, link, &clink->ptr);
        BLI_addtail(clb, clink);
      }

      break;
    }
    default: {
      if (len == 0) {
        fprintf(stderr, "%s.%s: unknown type for parameter %s\n", tid, fid, pid);
      }
      else {
        fprintf(stderr, "%s.%s: unknown array type for parameter %s\n", tid, fid, pid);
      }

      return -1;
    }
  }

  return 0;
}

int RNA_function_call_direct_va(bContext *C,
                                ReportList *reports,
                                PointerRNA *ptr,
                                FunctionRNA *func,
                                const char *format,
                                va_list args)
{
  PointerRNA funcptr;
  ParameterList parms;
  ParameterIterator iter;
  PropertyRNA *pret, *parm;
  PropertyType type;
  int i, ofs, flen, flag_parameter, len, alen, err = 0;
  const char *tid, *fid, *pid = NULL;
  char ftype;
  void **retdata = NULL;

  RNA_pointer_create(NULL, &RNA_Function, func, &funcptr);

  tid = RNA_struct_identifier(ptr->type);
  fid = RNA_function_identifier(func);
  pret = func->c_ret;
  flen = strlen(format);

  RNA_parameter_list_create(&parms, ptr, func);
  RNA_parameter_list_begin(&parms, &iter);

  for (i = 0, ofs = 0; iter.valid; RNA_parameter_list_next(&iter), i++) {
    parm = iter.parm;
    flag_parameter = RNA_parameter_flag(parm);

    if (parm == pret) {
      retdata = iter.data;
      continue;
    }
    else if (flag_parameter & PARM_OUTPUT) {
      continue;
    }

    pid = RNA_property_identifier(parm);

    if (ofs >= flen || format[ofs] == 'N') {
      if (parm->flag_parameter & PARM_REQUIRED) {
        err = -1;
        fprintf(stderr, "%s.%s: missing required parameter %s\n", tid, fid, pid);
        break;
      }
      ofs++;
      continue;
    }

    type = RNA_property_type(parm);
    ftype = format[ofs++];
    len = RNA_property_array_length(&funcptr, parm);
    alen = rna_function_format_array_length(format, ofs, flen);

    if (len != alen) {
      err = -1;
      fprintf(stderr,
              "%s.%s: for parameter %s, "
              "was expecting an array of %i elements, "
              "passed %i elements instead\n",
              tid,
              fid,
              pid,
              len,
              alen);
      break;
    }

    switch (type) {
      case PROP_BOOLEAN:
      case PROP_INT:
      case PROP_ENUM: {
        int arg = va_arg(args, int);
        err = rna_function_parameter_parse(
            &funcptr, parm, type, ftype, len, iter.data, &arg, NULL, tid, fid, pid);
        break;
      }
      case PROP_FLOAT: {
        double arg = va_arg(args, double);
        err = rna_function_parameter_parse(
            &funcptr, parm, type, ftype, len, iter.data, &arg, NULL, tid, fid, pid);
        break;
      }
      case PROP_STRING: {
        const char *arg = va_arg(args, char *);
        err = rna_function_parameter_parse(
            &funcptr, parm, type, ftype, len, iter.data, &arg, NULL, tid, fid, pid);
        break;
      }
      case PROP_POINTER: {
        StructRNA *srna = va_arg(args, StructRNA *);
        void *arg = va_arg(args, void *);
        err = rna_function_parameter_parse(
            &funcptr, parm, type, ftype, len, iter.data, &arg, srna, tid, fid, pid);
        break;
      }
      case PROP_COLLECTION: {
        StructRNA *srna = va_arg(args, StructRNA *);
        ListBase *arg = va_arg(args, ListBase *);
        err = rna_function_parameter_parse(
            &funcptr, parm, type, ftype, len, iter.data, &arg, srna, tid, fid, pid);
        break;
      }
      default: {
        /* handle errors */
        err = rna_function_parameter_parse(
            &funcptr, parm, type, ftype, len, iter.data, NULL, NULL, tid, fid, pid);
        break;
      }
    }

    if (err != 0) {
      break;
    }
  }

  if (err == 0) {
    err = RNA_function_call(C, reports, ptr, func, &parms);
  }

  /* XXX throw error when more parameters than those needed are passed or leave silent? */
  if (err == 0 && pret && ofs < flen && format[ofs++] == 'R') {
    parm = pret;

    type = RNA_property_type(parm);
    ftype = format[ofs++];
    len = RNA_property_array_length(&funcptr, parm);
    alen = rna_function_format_array_length(format, ofs, flen);

    if (len != alen) {
      err = -1;
      fprintf(stderr,
              "%s.%s: for return parameter %s, "
              "was expecting an array of %i elements, passed %i elements instead\n",
              tid,
              fid,
              pid,
              len,
              alen);
    }
    else {
      switch (type) {
        case PROP_BOOLEAN:
        case PROP_INT:
        case PROP_ENUM: {
          int *arg = va_arg(args, int *);
          err = rna_function_parameter_parse(
              &funcptr, parm, type, ftype, len, arg, retdata, NULL, tid, fid, pid);
          break;
        }
        case PROP_FLOAT: {
          float *arg = va_arg(args, float *);
          err = rna_function_parameter_parse(
              &funcptr, parm, type, ftype, len, arg, retdata, NULL, tid, fid, pid);
          break;
        }
        case PROP_STRING: {
          char **arg = va_arg(args, char **);
          err = rna_function_parameter_parse(
              &funcptr, parm, type, ftype, len, arg, retdata, NULL, tid, fid, pid);
          break;
        }
        case PROP_POINTER: {
          StructRNA *srna = va_arg(args, StructRNA *);
          void **arg = va_arg(args, void **);
          err = rna_function_parameter_parse(
              &funcptr, parm, type, ftype, len, arg, retdata, srna, tid, fid, pid);
          break;
        }
        case PROP_COLLECTION: {
          StructRNA *srna = va_arg(args, StructRNA *);
          ListBase **arg = va_arg(args, ListBase **);
          err = rna_function_parameter_parse(
              &funcptr, parm, type, ftype, len, arg, retdata, srna, tid, fid, pid);
          break;
        }
        default: {
          /* handle errors */
          err = rna_function_parameter_parse(
              &funcptr, parm, type, ftype, len, NULL, NULL, NULL, tid, fid, pid);
          break;
        }
      }
    }
  }

  RNA_parameter_list_end(&iter);
  RNA_parameter_list_free(&parms);

  return err;
}

int RNA_function_call_direct_va_lookup(bContext *C,
                                       ReportList *reports,
                                       PointerRNA *ptr,
                                       const char *identifier,
                                       const char *format,
                                       va_list args)
{
  FunctionRNA *func;

  func = RNA_struct_find_function(ptr->type, identifier);

  if (func) {
    return RNA_function_call_direct_va(C, reports, ptr, func, format, args);
  }

  return 0;
}

const char *RNA_translate_ui_text(
    const char *text, const char *text_ctxt, StructRNA *type, PropertyRNA *prop, int translate)
{
  return rna_translate_ui_text(text, text_ctxt, type, prop, translate);
}

bool RNA_property_reset(PointerRNA *ptr, PropertyRNA *prop, int index)
{
  int len;

  /* get the length of the array to work with */
  len = RNA_property_array_length(ptr, prop);

  /* get and set the default values as appropriate for the various types */
  switch (RNA_property_type(prop)) {
    case PROP_BOOLEAN:
      if (len) {
        if (index == -1) {
          bool *tmparray = MEM_callocN(sizeof(bool) * len, "reset_defaults - boolean");

          RNA_property_boolean_get_default_array(ptr, prop, tmparray);
          RNA_property_boolean_set_array(ptr, prop, tmparray);

          MEM_freeN(tmparray);
        }
        else {
          int value = RNA_property_boolean_get_default_index(ptr, prop, index);
          RNA_property_boolean_set_index(ptr, prop, index, value);
        }
      }
      else {
        int value = RNA_property_boolean_get_default(ptr, prop);
        RNA_property_boolean_set(ptr, prop, value);
      }
      return true;
    case PROP_INT:
      if (len) {
        if (index == -1) {
          int *tmparray = MEM_callocN(sizeof(int) * len, "reset_defaults - int");

          RNA_property_int_get_default_array(ptr, prop, tmparray);
          RNA_property_int_set_array(ptr, prop, tmparray);

          MEM_freeN(tmparray);
        }
        else {
          int value = RNA_property_int_get_default_index(ptr, prop, index);
          RNA_property_int_set_index(ptr, prop, index, value);
        }
      }
      else {
        int value = RNA_property_int_get_default(ptr, prop);
        RNA_property_int_set(ptr, prop, value);
      }
      return true;
    case PROP_FLOAT:
      if (len) {
        if (index == -1) {
          float *tmparray = MEM_callocN(sizeof(float) * len, "reset_defaults - float");

          RNA_property_float_get_default_array(ptr, prop, tmparray);
          RNA_property_float_set_array(ptr, prop, tmparray);

          MEM_freeN(tmparray);
        }
        else {
          float value = RNA_property_float_get_default_index(ptr, prop, index);
          RNA_property_float_set_index(ptr, prop, index, value);
        }
      }
      else {
        float value = RNA_property_float_get_default(ptr, prop);
        RNA_property_float_set(ptr, prop, value);
      }
      return true;
    case PROP_ENUM: {
      int value = RNA_property_enum_get_default(ptr, prop);
      RNA_property_enum_set(ptr, prop, value);
      return true;
    }

    case PROP_STRING: {
      char *value = RNA_property_string_get_default_alloc(ptr, prop, NULL, 0);
      RNA_property_string_set(ptr, prop, value);
      MEM_freeN(value);
      return true;
    }

    case PROP_POINTER: {
      PointerRNA value = RNA_property_pointer_get_default(ptr, prop);
      RNA_property_pointer_set(ptr, prop, value, NULL);
      return true;
    }

    default:
      /* FIXME: are there still any cases that haven't been handled?
       * comment out "default" block to check :) */
      return false;
  }
}

bool RNA_property_assign_default(PointerRNA *ptr, PropertyRNA *prop)
{
  if (!RNA_property_is_idprop(prop) || RNA_property_array_check(prop)) {
    return false;
  }

  /* get and set the default values as appropriate for the various types */
  switch (RNA_property_type(prop)) {
    case PROP_INT: {
      int value = RNA_property_int_get(ptr, prop);
      return RNA_property_int_set_default(ptr, prop, value);
    }

    case PROP_FLOAT: {
      float value = RNA_property_float_get(ptr, prop);
      return RNA_property_float_set_default(ptr, prop, value);
    }

    default:
      return false;
  }
}

/* use RNA_warning macro which includes __func__ suffix */
void _RNA_warning(const char *format, ...)
{
  va_list args;

  va_start(args, format);
  vprintf(format, args);
  va_end(args);

  /* gcc macro adds '\n', but cant use for other compilers */
#ifndef __GNUC__
  fputc('\n', stdout);
#endif

#ifdef WITH_PYTHON
  {
    extern void PyC_LineSpit(void);
    PyC_LineSpit();
  }
#endif
}

bool RNA_path_resolved_create(PointerRNA *ptr,
                              struct PropertyRNA *prop,
                              const int prop_index,
                              PathResolvedRNA *r_anim_rna)
{
  int array_len = RNA_property_array_length(ptr, prop);

  if ((array_len == 0) || (prop_index < array_len)) {
    r_anim_rna->ptr = *ptr;
    r_anim_rna->prop = prop;
    r_anim_rna->prop_index = array_len ? prop_index : -1;

    return true;
  }
  else {
    return false;
  }
}

static char rna_struct_state_owner[64];
void RNA_struct_state_owner_set(const char *name)
{
  if (name) {
    BLI_strncpy(rna_struct_state_owner, name, sizeof(rna_struct_state_owner));
  }
  else {
    rna_struct_state_owner[0] = '\0';
  }
}

const char *RNA_struct_state_owner_get(void)
{
  if (rna_struct_state_owner[0]) {
    return rna_struct_state_owner;
  }
  return NULL;
}
